diff --git a/.devcontainer/api/devcontainer.json b/.devcontainer/api/devcontainer.json new file mode 100644 index 000000000..f1a0fef12 --- /dev/null +++ b/.devcontainer/api/devcontainer.json @@ -0,0 +1,85 @@ +{ + "name": "Tracker API", + "image": "mcr.microsoft.com/devcontainers/javascript-node:20", + + "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/tracker,type=bind,consistency=cached", + "workspaceFolder": "/workspaces/tracker/api", + + "mounts": ["source=${localWorkspaceFolder},target=/workspaces/tracker,type=bind,consistency=cached"], + + "containerEnv": { + "NODE_ENV": "development", + "DB_URL": "http://localhost:8529", + "DB_NAME": "track_dmarc", + "DB_PASS": "test", + "NATS_URL": "nats://localhost:4222", + "NODE_OPTIONS": "--dns-result-order=ipv4first", + "AUTH_TOKEN_EXPIRY": "1d", + "REFRESH_TOKEN_EXPIRY": "30d", + "SIGN_IN_KEY": "devcontainer-sign-in-key-not-for-production", + "REFRESH_KEY": "devcontainer-refresh-key-not-for-production", + "AUTHENTICATED_KEY": "devcontainer-auth-key-not-for-production", + "CIPHER_KEY": "devcontainer-cipher-key-not-for-prod", + "HASHING_SALT": "12", + "LOGIN_REQUIRED": "false", + "DEPTH_LIMIT": "10", + "COST_LIMIT": "5000", + "SCALAR_COST": "1", + "OBJECT_COST": "1", + "LIST_FACTOR": "10", + "TRACING_ENABLED": "false", + "NOTIFICATION_API_KEY": "devcontainer-notification-api-key", + "NOTIFICATION_API_URL": "http://localhost:9999", + "NOTIFICATION_AUTHENTICATE_EMAIL_ID": "devcontainer-authenticate-email-id", + "NOTIFICATION_AUTHENTICATE_TEXT_ID": "devcontainer-authenticate-text-id", + "NOTIFICATION_ORG_INVITE_BILINGUAL": "devcontainer-org-invite-bilingual-id", + "NOTIFICATION_ORG_INVITE_CREATE_ACCOUNT_BILINGUAL": "devcontainer-org-invite-create-account-bilingual-id", + "NOTIFICATION_ORG_INVITE_REQUEST_BILINGUAL": "devcontainer-org-invite-request-bilingual-id", + "NOTIFICATION_PASSWORD_RESET_BILINGUAL": "devcontainer-password-reset-bilingual-id", + "NOTIFICATION_TWO_FACTOR_CODE_EN": "devcontainer-two-factor-code-en-id", + "NOTIFICATION_TWO_FACTOR_CODE_FR": "devcontainer-two-factor-code-fr-id", + "NOTIFICATION_VERIFICATION_EMAIL_BILINGUAL": "devcontainer-verification-email-bilingual-id", + "SERVICE_ACCOUNT_EMAIL": "dev@example.com" + }, + + "forwardPorts": [4000, 8529, 4222], + "portsAttributes": { + "4000": { + "label": "GraphQL API", + "onAutoForward": "notify" + }, + "8529": { + "label": "ArangoDB Web UI" + }, + "4222": { + "label": "NATS" + } + }, + + "postCreateCommand": "npm install && [ ! -f .env ] && cp .env.example .env || true", + + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "moby": false + } + }, + + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "apollographql.vscode-apollo", + "lokalise.i18n-ally", + "Orta.vscode-jest" + ], + "settings": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "eslint.workingDirectories": ["."] + } + } + }, + + "remoteUser": "node" +} diff --git a/.devcontainer/frontend/devcontainer.json b/.devcontainer/frontend/devcontainer.json new file mode 100644 index 000000000..77ee85211 --- /dev/null +++ b/.devcontainer/frontend/devcontainer.json @@ -0,0 +1,43 @@ +{ + "name": "Tracker Frontend", + "image": "mcr.microsoft.com/devcontainers/javascript-node:20", + + // Set the workspace to the frontend subdirectory of the monorepo + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}/frontend", + + "forwardPorts": [3000, 3001], + "portsAttributes": { + "3000": { + "label": "Frontend Dev Server", + "onAutoForward": "openBrowser" + }, + "3001": { + "label": "Webpack Dev Server (internal)" + } + }, + + // Install dependencies after the container is created + "postCreateCommand": "npm install", + + // Start the dev server automatically when the container starts + "postStartCommand": "npm run dev", + + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "dsznajder.es7-react-js-snippets", + "apollographql.vscode-apollo", + "lokalise.i18n-ally" + ], + "settings": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "eslint.workingDirectories": ["./frontend"] + } + } + }, + + "remoteUser": "node" +} diff --git a/.devcontainer/scanners/devcontainer.json b/.devcontainer/scanners/devcontainer.json new file mode 100644 index 000000000..336786533 --- /dev/null +++ b/.devcontainer/scanners/devcontainer.json @@ -0,0 +1,46 @@ +{ + "name": "Tracker Scanners", + "dockerComposeFile": "docker-compose.yml", + + // The service in docker-compose that VS Code / Codespaces attaches to + "service": "dev", + + // docker-compose.yml mounts the repo root at /workspaces, so scanners/ is at /workspaces/scanners. + "workspaceFolder": "/workspaces/scanners", + + "forwardPorts": [8529, 8222], + "portsAttributes": { + "8529": { + "label": "ArangoDB Web UI" + }, + "8222": { + "label": "NATS Monitor" + } + }, + + // Install the NATS CLI for injecting test messages, then set up a venv per service. + // + // Each service pins its own Python version. The Microsoft Python devcontainer image + // ships with pyenv, so we install the versions that differ from the container's + // default (3.14) and create isolated venvs per service directory. + // + // python-dotenv will not override env vars already set in docker-compose, so the + // container-friendly host names (arangodb, nats) take precedence over any localhost + // values in local .env files. + "postCreateCommand": "bash /workspaces/.devcontainer/scanners/setup.sh", + + "customizations": { + "vscode": { + "extensions": ["ms-python.python", "ms-python.vscode-pylance", "ms-python.debugpy", "ms-python.black-formatter"], + "settings": { + "editor.formatOnSave": true, + "python.defaultInterpreterPath": "${workspaceFolder}/dns-scanner/.venv/bin/python", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + } + } + } + }, + + "remoteUser": "vscode" +} diff --git a/.devcontainer/scanners/docker-compose.yml b/.devcontainer/scanners/docker-compose.yml new file mode 100644 index 000000000..2b8cd1217 --- /dev/null +++ b/.devcontainer/scanners/docker-compose.yml @@ -0,0 +1,89 @@ +version: "3.8" + +services: + dev: + image: mcr.microsoft.com/devcontainers/python:dev-3.14-bookworm + volumes: + # Mount the monorepo root so all relative paths within the project work + - ../..:/workspaces:cached + command: sleep infinity + environment: + # Override localhost defaults so services connect to named containers + - DB_USER=root + - DB_PASS=test + - DB_NAME=track_dmarc + - DB_URL=http://arangodb:8529 + - NATS_SERVERS=nats://nats:4222 + depends_on: + - arangodb + - nats + networks: + - tracker-dev + + arangodb: + image: arangodb:3.12.1 + environment: + - ARANGO_ROOT_PASSWORD=test + volumes: + - arangodb-data:/var/lib/arangodb3 + networks: + - tracker-dev + + nats: + image: nats:2.10.16-scratch + command: -js + networks: + - tracker-dev + + nats-init: + image: natsio/nats-box:latest + depends_on: + - nats + volumes: + - ../../k8s/infrastructure/bases/nats/stream-config.json:/stream-config/stream-config.json + entrypoint: > + /bin/sh -c ' + set -e + echo "[nats-stream-init] Waiting for NATS..." + until nc -z nats 4222; do + sleep 1 + done + sleep 2 + + echo "[nats-stream-init] Attempting to add SCANS stream..." + if nats stream add SCANS --config /stream-config/stream-config.json --server nats://nats:4222; then + echo "[nats-stream-init] SCANS stream added successfully." + else + echo "[nats-stream-init] Add failed, attempting to edit SCANS stream..." + if nats stream edit SCANS --config /stream-config/stream-config.json --server nats://nats:4222 --force; then + echo "[nats-stream-init] SCANS stream edited successfully." + else + echo "[nats-stream-init] ERROR: Could not add or edit SCANS stream!" + exit 1 + fi + fi + + echo "[nats-stream-init] Attempting to add WEB_SCANNER_IPS KV bucket..." + if nats kv add "WEB_SCANNER_IPS" --ttl="5m" --server nats://nats:4222; then + echo "[nats-stream-init] WEB_SCANNER_IPS KV bucket added successfully." + else + echo "[nats-stream-init] Add failed, attempting to edit WEB_SCANNER_IPS KV bucket..." + if nats kv edit "WEB_SCANNER_IPS" --ttl="5m" --server nats://nats:4222; then + echo "[nats-stream-init] WEB_SCANNER_IPS KV bucket edited successfully." + else + echo "[nats-stream-init] ERROR: Could not add or edit WEB_SCANNER_IPS KV bucket!" + exit 1 + fi + fi + + echo "[nats-stream-init] Stream initialization complete." + exec sleep infinity + ' + networks: + - tracker-dev + +networks: + tracker-dev: + +volumes: + arangodb-data: diff --git a/.devcontainer/scanners/setup.sh b/.devcontainer/scanners/setup.sh new file mode 100644 index 000000000..4bb1ecfa1 --- /dev/null +++ b/.devcontainer/scanners/setup.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +set -euo pipefail + +# The script lives at .devcontainer/scanners/setup.sh — two dirs up is the repo root +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +SCANNERS_DIR="$REPO_ROOT/scanners" + +# --------------------------------------------------------------------------- +# NATS CLI — useful for publishing test messages and inspecting streams +# --------------------------------------------------------------------------- +echo ">>> Installing NATS CLI..." +NATS_VERSION="0.2.2" +ARCH="$(dpkg --print-architecture)" # amd64 or arm64 +curl -fsSL "https://github.com/nats-io/natscli/releases/download/v${NATS_VERSION}/nats-${NATS_VERSION}-linux-${ARCH}.zip" \ + -o /tmp/nats.zip +unzip -q /tmp/nats.zip -d /tmp/nats +sudo mv /tmp/nats/nats-*/nats /usr/local/bin/nats +rm -rf /tmp/nats /tmp/nats.zip +echo " nats $(nats --version)" + +# --------------------------------------------------------------------------- +# pyenv — install if not already present, then bootstrap for this session. +# Each scanner service pins its own Python version in its Dockerfile; we +# use pyenv to create isolated venvs per service without touching system Python. +# --------------------------------------------------------------------------- +export PYENV_ROOT="$HOME/.pyenv" +if [ ! -x "$PYENV_ROOT/bin/pyenv" ]; then + echo ">>> Installing build dependencies..." + # Required to compile CPython (pyenv) and native Python extensions (e.g. pydantic-core) + sudo apt-get update -qq + sudo apt-get install -y -qq \ + build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev \ + libsqlite3-dev libffi-dev liblzma-dev libncurses-dev + + echo ">>> Installing Rust (required for pydantic-core and other native extensions)..." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path + # Source immediately so cargo is available for the rest of this block + source "$HOME/.cargo/env" + + echo ">>> Installing pyenv..." + curl -fsSL https://pyenv.run | bash + + echo ">>> Installing rust-query-crlite (used by web-scanner for TLS revocation checks)..." + cargo install \ + --git https://github.com/mozilla/crlite rust-query-crlite \ + --tag v1.0.39 +fi + +# Make Rust and pyenv available for this session +export PATH="$HOME/.cargo/bin:$PATH" +export PATH="$PYENV_ROOT/bin:$PATH" +eval "$(pyenv init --path)" + +# Map service → Python version (sourced from each service's Dockerfile) +declare -A SERVICE_PYTHON=( + [dns-scanner]="3.13.5" + [dns-processor]="3.14.2" + [web-scanner]="3.12.8" + [web-processor]="3.14.2" +) + +for service in "${!SERVICE_PYTHON[@]}"; do + version="${SERVICE_PYTHON[$service]}" + svc_dir="$SCANNERS_DIR/$service" + + echo ">>> [$service] Setting up Python $version venv..." + + # Install the version if pyenv doesn't have it yet. + # --enable-shared is required for maturin/pyo3 (e.g. pydantic-core) to link + # against libpython. Without it, maturin ignores the venv and falls back to + # the system Python, causing version mismatches and build failures. + if ! pyenv versions --bare | grep -qx "$version"; then + PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install "$version" + fi + + # Create venv using the pinned version + "$PYENV_ROOT/versions/$version/bin/python" -m venv "$svc_dir/.venv" + + # Install dependencies + "$svc_dir/.venv/bin/pip" install --quiet --upgrade pip + "$svc_dir/.venv/bin/pip" install --quiet -r "$svc_dir/requirements.txt" + + # Copy .env.example → .env if no .env exists yet + if [ ! -f "$svc_dir/.env" ] && [ -f "$svc_dir/.env.example" ]; then + cp "$svc_dir/.env.example" "$svc_dir/.env" + echo " Copied .env.example → .env (fill in secrets before running)" + fi + + echo " Done." +done + +echo "" +echo "Setup complete. Tips:" +echo " • cd into a service directory and activate its venv: source .venv/bin/activate" +echo " • Run the service: python service.py" +echo " • Publish a test scan request via NATS CLI:" +echo " nats pub --server nats://nats:4222 scans.requests '{\"domain\": \"example.com\"}'" +echo " • Inspect the SCANS stream:" +echo " nats stream info SCANS --server nats://nats:4222" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4be0c4717..596e4131f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,204 +1,383 @@ version: 2 + updates: -- package-ecosystem: docker - directory: "/api" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: npm - directory: "/api" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/ci" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/clients/python" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/frontend" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: npm - directory: "/frontend" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/scanners/web-scanner" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/scanners/web-scanner" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/auto-scan" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/auto-scan" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/core" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/core" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/dmarc-report" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: npm - directory: "/services/dmarc-report" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/result-queue" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/result-queue" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/scanners/dns" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/scanners/dns" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/scanners/https" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/scanners/https" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/results" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/results" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/scanners/ssl" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/scanners/ssl" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/scan-queue" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: pip - directory: "/services/scan-queue" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: docker - directory: "/services/super-admin" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: npm - directory: "/services/super-admin" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 - -- package-ecosystem: npm - directory: "/scripts/insert-json" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 5 + # Monday — frontend (standalone, largest dep file) + - package-ecosystem: docker + directory: "/frontend" + schedule: + interval: weekly + day: monday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/frontend" + schedule: + interval: weekly + day: monday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + # Tuesday — api (standalone, large dep file) + - package-ecosystem: docker + directory: "/api" + schedule: + interval: weekly + day: tuesday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/api" + schedule: + interval: weekly + day: tuesday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + # Wednesday — dmarc-report + super-admin (medium services) + - package-ecosystem: docker + directory: "/services/dmarc-report" + schedule: + interval: weekly + day: wednesday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/services/dmarc-report" + schedule: + interval: weekly + day: wednesday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/services/super-admin" + schedule: + interval: weekly + day: wednesday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/services/super-admin" + schedule: + interval: weekly + day: wednesday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + # Thursday — domain-cleanup + org-footprint (medium services) + - package-ecosystem: docker + directory: "/services/domain-cleanup" + schedule: + interval: weekly + day: thursday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/services/domain-cleanup" + schedule: + interval: weekly + day: thursday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/services/org-footprint" + schedule: + interval: weekly + day: thursday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/services/org-footprint" + schedule: + interval: weekly + day: thursday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + # Friday — progress-report + domain-dispatcher (medium services) + - package-ecosystem: docker + directory: "/services/progress-report" + schedule: + interval: weekly + day: friday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/services/progress-report" + schedule: + interval: weekly + day: friday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/scanners/domain-dispatcher" + schedule: + interval: weekly + day: friday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: npm + directory: "/scanners/domain-dispatcher" + schedule: + interval: weekly + day: friday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + # Saturday — web-scanner + dns-scanner + domain-discovery (small scanners) + - package-ecosystem: docker + directory: "/scanners/web-scanner" + schedule: + interval: weekly + day: saturday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/scanners/web-scanner" + schedule: + interval: weekly + day: saturday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/scanners/dns-scanner" + schedule: + interval: weekly + day: saturday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/scanners/dns-scanner" + schedule: + interval: weekly + day: saturday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/scanners/domain-discovery" + schedule: + interval: weekly + day: saturday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/scanners/domain-discovery" + schedule: + interval: weekly + day: saturday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + # Sunday — update-selectors + summaries + uptime + azure-defender-easm (smallest services) + - package-ecosystem: docker + directory: "/services/update-selectors" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/services/update-selectors" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/services/summaries" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/services/summaries" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/services/uptime" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/services/uptime" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/azure-defender-easm/import-easm-additional-findings" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/azure-defender-easm/import-easm-additional-findings" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/azure-defender-easm/label-known-easm-assets" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/azure-defender-easm/label-known-easm-assets" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 + + - package-ecosystem: docker + directory: "/azure-defender-easm/add-domain-to-easm" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + + - package-ecosystem: pip + directory: "/azure-defender-easm/add-domain-to-easm" + schedule: + interval: weekly + day: sunday + time: "11:00" + open-pull-requests-limit: 3 + cooldown: + default-days: 7 + semver-minor-days: 14 + semver-major-days: 30 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 916f5fcd6..b3d26e2bc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,50 +1,50 @@ -name: "CodeQL" +name: CodeQL - Analysis on: + push: + branches: ["master"] + pull_request: + branches: ["master"] schedule: - # Run every Tuesday + # Run every Tuesday at 00:00 UTC - cron: "0 0 * * 2" +concurrency: + group: codeql-${{ github.ref }} + cancel-in-progress: true + +permissions: + security-events: write + actions: read + contents: read + jobs: analyze: - name: Analyze + name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest + timeout-minutes: 60 strategy: fail-fast: false matrix: - language: [ 'javascript', 'python' ] + language: + - javascript-typescript + - python + - actions steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 - + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + # Built-in query suites: security-extended includes more rules than + # the default suite and is recommended for security-focused scanning. + queries: security-extended + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/dependabot-gcbrun.yml b/.github/workflows/dependabot-gcbrun.yml new file mode 100644 index 000000000..f35c2b6bd --- /dev/null +++ b/.github/workflows/dependabot-gcbrun.yml @@ -0,0 +1,21 @@ +name: Trigger Cloud Build for Dependabot +on: + pull_request_target: + types: [opened, synchronize, reopened] + +jobs: + gcbrun: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - name: Post /gcbrun as collaborator + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GCB_TRIGGER_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '/gcbrun' + }) diff --git a/.github/workflows/frontend-build.yaml b/.github/workflows/frontend-build.yaml deleted file mode 100644 index f128b9aa3..000000000 --- a/.github/workflows/frontend-build.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Build frontend - -on: - workflow_dispatch: - push: - branches: [ "master" ] - paths: 'frontend/**' - pull_request: - branches: [ "master" ] - paths: 'frontend/**' - - -jobs: - test: -# runs-on: - runs-on: ubuntu-latest - -# strategy: -# matrix: -# node-version: [12.x, 14.x, 16.x] -# # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - run: echo ${GITHUB_REF_NAME} -# - uses: actions/checkout@v3 -# - name: Use Node.js ${{ matrix.node-version }} -# uses: actions/setup-node@v3 -# with: -# node-version: ${{ matrix.node-version }} -# cache: 'npm' -# - run: npm ci -# - run: npm run build --if-present -# - run: npm test - build: - if: github.ref_name == 'master' - runs-on: ubuntu-latest - steps: - - run: echo IN MASTER ${GITHUB_REF_NAME} diff --git a/.github/workflows/set-maintenance.yml b/.github/workflows/set-maintenance.yml index 3ab480ca4..fb86a1b0e 100644 --- a/.github/workflows/set-maintenance.yml +++ b/.github/workflows/set-maintenance.yml @@ -28,6 +28,8 @@ on: jobs: update_value: + permissions: + contents: write # The type of runner that the job will run on runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 17bafb7d7..f329dc33a 100755 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ api/api.current.graphql **/*.env **/.venv venv +**/lib64 +**/pyvenv.cfg # Linting **/.mypy_cache/ @@ -42,6 +44,7 @@ scanner/logs # IDE Files .idea/ .vscode/ +*.code-workspace # all dist directories **/dist diff --git a/Makefile b/Makefile index 7570bf98f..d45e26e47 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,16 @@ endef .PHONY: cluster cluster: - gcloud beta container --project "$(project)" clusters create "$(name)" --region "$(region)" --no-enable-basic-auth --release-channel "rapid" --cluster-version "1.24.8-gke.2000" --machine-type "n2d-standard-4" --image-type "COS_CONTAINERD" --disk-type "pd-standard" --disk-size "50" --metadata disable-legacy-endpoints=true --service-account "gke-node-service-account@track-compliance.iam.gserviceaccount.com" --num-nodes "2" --logging=SYSTEM,WORKLOAD --monitoring=SYSTEM --enable-ip-alias --network "projects/track-compliance/global/networks/default" --subnetwork "projects/track-compliance/regions/northamerica-northeast1/subnetworks/default" --no-enable-master-authorized-networks --addons HorizontalPodAutoscaling,HttpLoadBalancing --enable-autoupgrade --enable-autorepair --max-surge-upgrade 1 --max-unavailable-upgrade 0 --workload-pool "track-compliance.svc.id.goog" --enable-shielded-nodes --enable-dataplane-v2 --shielded-secure-boot --shielded-integrity-monitoring + gcloud container --project "$(project)" clusters create "$(name)" --region "$(region)" --no-enable-basic-auth --release-channel "stable" --cluster-version "1.24.11-gke.1000" --machine-type "n2d-standard-4" --image-type "COS_CONTAINERD" --disk-type "pd-standard" --disk-size "50" --metadata disable-legacy-endpoints=true --service-account "gke-least-priviledge-account@$(project).iam.gserviceaccount.com" --num-nodes "2" --logging=SYSTEM,WORKLOAD --monitoring=SYSTEM --enable-ip-alias --network "projects/$(project)/global/networks/default" --subnetwork "projects/$(project)/regions/northamerica-northeast1/subnetworks/default" --no-enable-master-authorized-networks --addons HorizontalPodAutoscaling,HttpLoadBalancing --enable-autoupgrade --enable-autorepair --max-surge-upgrade 1 --max-unavailable-upgrade 0 --workload-pool "$(project).svc.id.goog" --enable-shielded-nodes --enable-dataplane-v2 --shielded-secure-boot --shielded-integrity-monitoring + +.PHONY: gke-service-account +gke-service-account: + gcloud iam service-accounts create gke-least-priviledge-account --display-name="gke least priviledge account" + gcloud projects add-iam-policy-binding php-observatory --member "serviceAccount:gke-least-priviledge-account@php-observatory.iam.gserviceaccount.com" --role roles/logging.logWriter + gcloud projects add-iam-policy-binding php-observatory --member "serviceAccount:gke-least-priviledge-account@php-observatory.iam.gserviceaccount.com" --role roles/monitoring.metricWriter + gcloud projects add-iam-policy-binding php-observatory --member "serviceAccount:gke-least-priviledge-account@php-observatory.iam.gserviceaccount.com" --role roles/monitoring.viewer + gcloud projects add-iam-policy-binding php-observatory --member "serviceAccount:gke-least-priviledge-account@php-observatory.iam.gserviceaccount.com" --role roles/stackdriver.resourceMetadata.writer + .PHONY: secrets secrets: @@ -46,7 +55,7 @@ update-flux: .PHONY: update-istio update-istio: - istioctl manifest generate --dry-run > k8s/infrastructure/bases/istio/platform/crds.yaml + istioctl manifest generate -f k8s/infrastructure/bases/istio/istio-operator.yaml --dry-run > k8s/infrastructure/bases/istio/platform/crds.yaml .PHONY: print-arango-deployment print-arango-deployment: @@ -80,6 +89,11 @@ scan: kubectl delete job domain-dispatcher-manual -n scanners --ignore-not-found && kubectl create job domain-dispatcher-manual --from=cronjob/domain-dispatcher -n scanners +.PHONY: detect-decay +detect-decay: + kubectl delete job detect-decay-manual -n scanners --ignore-not-found && + kubectl create job detect-decay-manual --from=cronjob/detect-decay -n scanners + .PHONY: scanners scanners: kustomize build scanners | kubectl apply -f - diff --git a/README.md b/README.md index 0acd8f560..729dd1a9d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ As is common with microservices projects, this repository is organized in the [m The [ci](ci/README.md) folder contains an image used in the CI process, but the main event is the next three folders: -The [frontend](frontend/README.md) and [api](api/README.md) folders contain the two main parts parts of the application. +The [frontend](frontend/README.md) and [api](api/README.md) folders contain the two main parts of the application. The [k8s](k8s/README.md) folder contain the Kubernetes configurations needed to continuously deploy the tracker on the cloud provider of your choice. @@ -42,7 +42,7 @@ The scripts folder is a dumping ground for various utility scripts and codemods. ## Running it locally -Running Tracker locally takes a few commands and a lot of RAM. See the instructions in the [app folder](app/README.md) +Running Tracker locally takes a few commands and a lot of RAM. See the instructions in the [k8s folder](k8s/README.md) ## Deploying to the cloud @@ -68,7 +68,7 @@ $ make platform env= $ make app env= ``` -Tracker is now deployed. To add coninuous deployment functionality via [Flux](https://fluxcd.io/) (this will ensure the Tracker deployment stays up to date with all the latest changes), follow the instructions listed below. +Tracker is now deployed. To add continuous deployment functionality via [Flux](https://fluxcd.io/) (this will ensure the Tracker deployment stays up to date with all the latest changes), follow the instructions listed below. ### NOTE: Steps 1) and 2) are only required if the Tracker deployment should write back to this repository, updating image tags as necessary. diff --git a/api/.env.example b/api/.env.example index fdb28589c..d55a990ee 100644 --- a/api/.env.example +++ b/api/.env.example @@ -20,12 +20,11 @@ NOTIFICATION_ORG_INVITE_REQUEST_BILINGUAL= NOTIFICATION_PASSWORD_RESET_BILINGUAL= NOTIFICATION_TWO_FACTOR_CODE_EN= NOTIFICATION_TWO_FACTOR_CODE_FR= -NOTIFICATION_VERIFICATION_EMAIL_EN= -NOTIFICATION_VERIFICATION_EMAIL_FR= +NOTIFICATION_VERIFICATION_EMAIL_BILINGUAL= OBJECT_COST= REFRESH_KEY= REFRESH_TOKEN_EXPIRY= SCALAR_COST= SERVICE_ACCOUNT_EMAIL= SIGN_IN_KEY= -TRACING_ENABLED= \ No newline at end of file +TRACING_ENABLED= diff --git a/api/Dockerfile b/api/Dockerfile index 240093269..aacb3cd5f 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,7 +1,7 @@ # To build image: docker build --tag tracker-api:1.0 . # To run image: docker run --network=host --env-file ./.env tracker-api:1.0 # Build image -FROM node:alpine AS base +FROM node:20.20.0-alpine3.22 AS base WORKDIR /app @@ -13,7 +13,7 @@ RUN npm ci COPY ./src ./src COPY ./index.js . -COPY ./database.json . +COPY ./lingui.config.js . COPY ./.env.example . RUN npm run build @@ -29,7 +29,7 @@ COPY --from=builder /app/package.json . COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/.env.example . COPY --from=builder /app/index.js . -COPY --from=builder /app/database.json . +COPY --from=builder /app/lingui.config.js . COPY --from=builder /app/dist ./dist USER node diff --git a/api/README.md b/api/README.md index 2a92a093a..d676dd84e 100644 --- a/api/README.md +++ b/api/README.md @@ -11,7 +11,7 @@ npm install #### Running API Server -In accordance with the [12Factor app](https://12factor.net) philosophy, the server [draws it's config from the environment](https://12factor.net/config). It does based on a `.env` file that should exist in the root of the API folder which can be created with the following command, obviously modifying the test values shown to suit your setup. +In accordance with the [12Factor app](https://12factor.net) philosophy, the server [draws it's config from the environment](https://12factor.net/config). It does so based on a `.env` file that should exist in the root of the API folder which can be created with the following command, obviously modifying the test values shown to suit your setup. ```bash cat <<'EOF' > test.env @@ -76,7 +76,7 @@ npm run dev #### Running Tests -The tests require a copy of [ArangoDB](https://www.arangodb.com/) to be running locally. ArangoDB should have it's own .env file, and the value of the root password should align with the value of `DB_PASS` in the APIs `test.env` file. +The tests require a copy of [ArangoDB](https://www.arangodb.com/) to be running locally. ArangoDB should have its own .env file, and the value of the root password should align with the value of `DB_PASS` in the APIs `test.env` file. ```bash # Write the arango test credentials into an env file: diff --git a/api/cloudbuild.yaml b/api/cloudbuild.yaml index 32dc9b1be..c3ee4b9bf 100644 --- a/api/cloudbuild.yaml +++ b/api/cloudbuild.yaml @@ -5,26 +5,68 @@ steps: args: [ '-c', - 'docker run -d --network=cloudbuild -p=8529:8529 -e ARANGO_ROOT_PASSWORD=$_DB_PASS --name=arangodb arangodb/arangodb:3.10.4', + 'docker run -d --network=cloudbuild -p=8529:8529 -e ARANGO_ROOT_PASSWORD=$_DB_PASS --name=arangodb arangodb/arangodb:3.12.1', ] - name: mikewilliamson/wait-for id: wait_arango args: ['arangodb:8529'] - - name: node:alpine + - name: node:20-alpine id: install dir: api entrypoint: npm - args: ['ci', '--no-optional'] + args: ['ci'] - - name: node:alpine + - name: node:20-alpine id: lint dir: api entrypoint: npm args: ['run', lint] - - name: node:alpine + - name: node:20-alpine + id: lingui-extract + dir: api + entrypoint: ash + args: + - '-c' + - | + npm run extract | tee /workspace/lingui-extract-output.txt + + - name: node:20-alpine + id: lingui-translation-check + dir: api + entrypoint: ash + args: + - '-c' + - | + output=$( awk -F '│' '/fr/ { gsub(/^[ \t]+|[ \t]+$/, "", $4); print $4 }' /workspace/lingui-extract-output.txt ) + + # Check if there are any missed french translations + if [ "$output" -eq 0 ]; then + echo "All translations are up to date, proceeding." + else + echo "There are $output missed translations, please update them before proceeding." + exit 1 + fi + + - name: node:20-alpine + id: lingui-compile + dir: api + entrypoint: npm + args: ['run', 'compile'] + + - name: node:20-alpine + id: check-src-files + dir: api + entrypoint: ash + args: + - '-c' + - | + "=== checking src directory content ===" + ls -la src + + - name: node:20-alpine id: test dir: api entrypoint: npm @@ -47,8 +89,7 @@ steps: - NOTIFICATION_PASSWORD_RESET_BILINGUAL=$_NOTIFICATION_TEST_TEMPLATE_ID - NOTIFICATION_TWO_FACTOR_CODE_EN=$_NOTIFICATION_TEST_TEMPLATE_ID - NOTIFICATION_TWO_FACTOR_CODE_FR=$_NOTIFICATION_TEST_TEMPLATE_ID - - NOTIFICATION_VERIFICATION_EMAIL_EN=$_NOTIFICATION_TEST_TEMPLATE_ID - - NOTIFICATION_VERIFICATION_EMAIL_FR=$_NOTIFICATION_TEST_TEMPLATE_ID + - NOTIFICATION_VERIFICATION_EMAIL_BILINGUAL=$_NOTIFICATION_TEST_TEMPLATE_ID - NOTIFICATION_ORG_INVITE_BILINGUAL=$_NOTIFICATION_TEST_TEMPLATE_ID - DEPTH_LIMIT=$_DEPTH_LIMIT - COST_LIMIT=$_COST_LIMIT @@ -62,18 +103,7 @@ steps: - NATS_URL=$_NATS_URL - NOTIFICATION_ORG_INVITE_CREATE_ACCOUNT_BILINGUAL=$_NOTIFICATION_TEST_TEMPLATE_ID - SERVICE_ACCOUNT_EMAIL=$_SERVICE_ACCOUNT_EMAIL - - - name: node:alpine - id: lingui-extract - dir: api - entrypoint: npm - args: ['run', 'extract'] - - - name: node:alpine - id: lingui-compile - dir: api - entrypoint: npm - args: ['run', 'compile'] + - TRACKER_PRODUCTION=true - name: 'gcr.io/cloud-builders/docker' id: generate-image-name @@ -82,22 +112,17 @@ steps: args: - '-c' - | - echo "gcr.io/$PROJECT_ID/api-js:$BRANCH_NAME-$SHORT_SHA-$(date +%s)" > /workspace/imagename + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/api-js:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename - name: 'gcr.io/cloud-builders/docker' - id: build-if-master + id: build entrypoint: 'bash' dir: api args: - '-c' - | - if [[ "$BRANCH_NAME" == "master" ]] - then - image=$(cat /workspace/imagename) - docker build -t $image . - else - exit 0 - fi + image=$(cat /workspace/imagename) + docker build -t $image . - name: 'gcr.io/cloud-builders/docker' id: push-if-master diff --git a/api/database.json b/api/database.json deleted file mode 100644 index 3db08fbd6..000000000 --- a/api/database.json +++ /dev/null @@ -1,352 +0,0 @@ -{ - "type": "database", - "name": "{{dbname}}", - "url": "{{url}}", - "rootPassword": "{{rootPassword}}", - "options": [ - { "type": "user", "username": "{{username}}", "password": "{{password}}" }, - { - "type": "documentcollection", - "name": "users", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "organizations", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "domains", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "selectors", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "auditLogs", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "dns", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "web", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "webScan", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "dkimGuidanceTags", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "dmarcGuidanceTags", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "spfGuidanceTags", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "httpsGuidanceTags", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "sslGuidanceTags", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "chartSummaries", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "dmarcSummaries", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "aggregateGuidanceTags", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "scanSummaryCriteria", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "documentcollection", - "name": "chartSummaryCriteria", - "options": { - "replicationfactor": 3, - "writeconcern": 1, - "numberofshards": 6 - } - }, - { - "type": "documentcollection", - "name": "scanSummaries", - "options": { - "replicationfactor": 3, - "writeconcern": 1, - "numberofshards": 6 - } - }, - { - "type": "documentcollection", - "name": "organizationSummaries", - "options": { - "replicationfactor": 3, - "writeconcern": 1, - "numberofshards": 6 - } - }, - { - "type": "edgecollection", - "name": "domainsToSelectors", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "affiliations", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "claims", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "favourites", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "domainsDNS", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "domainsWeb", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "webToWebScans", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "ownership", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "edgecollection", - "name": "domainsToDmarcSummaries", - "options": { - "replicationFactor": 3, - "writeConcern": 1, - "numberOfShards": 6 - } - }, - { - "type": "delimiteranalyzer", - "name": "space-delimiter-analyzer", - "delimiter": " " - }, - { - "type": "searchview", - "name": "domainSearch", - "options": { - "links": { - "domains": { - "fields": { - "domain": { "analyzers": ["space-delimiter-analyzer"] } - } - } - } - } - }, - { - "type": "searchview", - "name": "organizationSearch", - "options": { - "links": { - "organizations": { - "fields": { - "orgDetails": { - "fields": { - "en": { - "fields": { - "acronym": { "analyzers": ["text_en"] }, - "name": { "analyzers": ["text_en"] } - } - }, - "fr": { - "fields": { - "acronym": { "analyzers": ["text_fr"] }, - "name": { "analyzers": ["text_fr"] } - } - } - } - } - } - } - } - } - }, - { - "type": "searchview", - "name": "userSearch", - "options": { - "links": { - "users": { - "fields": { - "displayName": { "analyzers": ["text_en"] }, - "userName": { "analyzers": ["text_en"] } - } - } - } - } - }, - { - "type": "searchview", - "name": "auditLogSearch", - "options": { - "links": { - "auditLogs": { - "fields": { - "initiatedBy": { - "fields": { - "userName": { "analyzers": ["text_en"] } - } - }, - "target": { - "fields": { - "resource": { "analyzers": ["text_en"] } - } - } - } - } - } - } - } - ] -} diff --git a/api/database.json b/api/database.json new file mode 120000 index 000000000..d19d64174 --- /dev/null +++ b/api/database.json @@ -0,0 +1 @@ +../database-migration/database.json \ No newline at end of file diff --git a/api/docker-compose.yaml b/api/docker-compose.yaml new file mode 100644 index 000000000..32f30d91f --- /dev/null +++ b/api/docker-compose.yaml @@ -0,0 +1,78 @@ +version: '3' + +services: + nats: + image: nats:2.10.16-scratch + container_name: nats + network_mode: "host" + command: -js + restart: always + + nats-box: + image: natsio/nats-box:latest + container_name: nats-box + depends_on: + - nats + network_mode: "host" + volumes: + - ../k8s/infrastructure/bases/nats/stream-config.json:/stream-config/stream-config.json + entrypoint: > + /bin/sh -c ' + set -e + echo "[nats-stream-init] Waiting for NATS..." + until nc -z localhost 4222; do + sleep 1 + done + + # Small additional delay to ensure JetStream is ready + sleep 2 + + # ==================== CREATE/UPDATE STREAM ==================== + echo "[nats-stream-init] Attempting to add SCANS stream..." + if nats stream add SCANS --config /stream-config/stream-config.json --server nats://localhost:4222; then + echo "[nats-stream-init] SCANS stream added successfully." + else + echo "[nats-stream-init] Add failed, attempting to edit SCANS stream..." + if nats stream edit SCANS --config /stream-config/stream-config.json --server nats://localhost:4222 --force; then + echo "[nats-stream-init] SCANS stream edited successfully." + else + echo "[nats-stream-init] ERROR: Could not add or edit SCANS stream!" + exit 1 + fi + fi + + # ==================== CREATE/UPDATE KV STORE ==================== + + echo "[nats-stream-init] Attempting to add WEB_SCANNER_IPS KV bucket..." + + if nats kv add "WEB_SCANNER_IPS" \ + --ttl="5m"; then + echo "[nats-stream-init] WEB_SCANNER_IPS KV bucket added successfully." + else + echo "[nats-stream-init] Add failed, attempting to edit WEB_SCANNER_IPS KV bucket..." + + if nats kv edit "WEB_SCANNER_IPS" \ + --ttl="5m"; then + echo "[nats-stream-init] WEB_SCANNER_IPS KV bucket edited successfully." + else + echo "[nats-stream-init] ERROR: Could not add or edit WEB_SCANNER_IPS KV bucket!" + exit 1 + fi + fi + + echo "[nats-stream-init] Stream initialization complete." + exec sleep infinity + ' + + arangodb: + image: arangodb:3.12.1 + container_name: arangodb + environment: + - ARANGO_ROOT_PASSWORD=test + network_mode: "host" + restart: unless-stopped + volumes: + - ${TRACKER_BACKUP_DIR:-arangodb-data}:/var/lib/arangodb3 + +volumes: + arangodb-data: diff --git a/api/index.js b/api/index.js index 944adf3f4..89ba00fc9 100644 --- a/api/index.js +++ b/api/index.js @@ -39,12 +39,7 @@ const { return db.beginTransaction(collections) } - const nc = await connect({ servers: NATS_URL }) - - const jsm = await nc.jetstreamManager() - - await jsm.streams.add({ name: 'domains', subjects: ['domains.*'] }) - await jsm.streams.add({ name: 'domain-discovery', subjects: ['domains.*.discovery'] }) + const nc = await connect({ servers: NATS_URL, maxReconnectAttempts: -1, reconnectTimeWait: 1000 }) // create a jetstream client: const js = nc.jetstream() @@ -95,6 +90,5 @@ const { await server.listen(PORT, (err) => { if (err) throw err console.log(`🚀 Server ready at http://localhost:${PORT}/graphql`) - console.log(`🚀 Subscriptions ready at ws://localhost:${PORT}/graphql`) }) })() diff --git a/api/jest.config.js b/api/jest.config.js index f76d1efeb..1aeda50c8 100644 --- a/api/jest.config.js +++ b/api/jest.config.js @@ -11,5 +11,8 @@ module.exports = { 'index.js', 'env.js', ], + moduleNameMapper: { + '^axios$': require.resolve('axios'), + }, testTimeout: 120000, } diff --git a/api/lingui.config.js b/api/lingui.config.js index bd5edec8f..1f1b28e62 100644 --- a/api/lingui.config.js +++ b/api/lingui.config.js @@ -5,7 +5,6 @@ module.exports = { include: ['src'], }, ], - extractBabelOptions: {}, fallbackLocales: { default: 'en', }, diff --git a/api/package-lock.json b/api/package-lock.json index 0195919b9..9ac7bc81d 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -9,63 +9,65 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@apollo/server": "^4.9.3", - "@lingui/core": "^3.13.0", + "@apollo/server": "^5.5.0", + "@as-integrations/express4": "^1.1.2", + "@lingui/core": "^4.13.0", + "accesscontrol": "^2.2.1", "arango-tools": "^0.6.0", - "arangojs": "^8.0.0", + "arangojs": "^10.2.2", "bcryptjs": "^2.4.3", - "body-parser": "^1.20.2", - "cookie-parser": "^1.4.6", + "body-parser": "^1.20.5", + "compression": "^1.8.1", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", "dataloader": "^2.0.0", "dotenv-safe": "^8.2.0", - "express": "^4.17.3", + "express": "^4.22.2", "express-request-language": "^1.1.15", - "graphql": "^16.8.1", + "graphql": "^16.12.0", "graphql-depth-limit": "^1.1.0", "graphql-redis-subscriptions": "^2.6.0", - "graphql-relay": "^0.10.0", - "graphql-scalars": "^1.22.4", + "graphql-relay": "^0.10.2", + "graphql-scalars": "^1.25.0", "graphql-subscriptions": "^2.0.0", "graphql-validation-complexity": "^0.4.2", "ioredis": "^4.28.3", "isomorphic-fetch": "^3.0.0", - "jsonwebtoken": "^9.0.0", + "jsonwebtoken": "^9.0.2", "make-plural": "^7.1.0", "moment": "^2.29.4", "ms": "^2.1.3", - "nats": "^2.7.0", - "notifications-node-client": "^7.0.3", - "subscriptions-transport-ws": "^0.11.0", + "nats": "^2.18.0", + "notifications-node-client": "^8.2.1", "url-slug": "^3.0.2", - "uuid": "^8.3.2", - "validator": "^13.7.0" + "uuid": "^11.1.1", + "validator": "^13.15.22" }, "devDependencies": { "@babel/cli": "^7.16.8", "@babel/core": "^7.16.7", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.8", - "@jest/test-sequencer": "^27.4.6", - "@lingui/cli": "^3.13.0", - "@lingui/loader": "^3.13.0", - "@lingui/macro": "^3.13.0", + "@jest/test-sequencer": "^30.3.0", + "@lingui/cli": "^5.4.1", + "@lingui/macro": "^4.13.0", "babel-core": "^7.0.0-bridge.0", - "babel-jest": "^27.4.6", + "babel-jest": "^30.3.0", "babel-plugin-macros": "^3.1.0", "babel-polyfill": "^6.26.0", - "eslint": "^7.32.0", + "eslint": "^8.57.1", "eslint-config-prettier": "^8.3.0", - "eslint-config-standard": "^16.0.3", + "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-jest": "^24.7.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.2.0", - "jest": "^27.4.7", + "eslint-plugin-jest": "^29.15.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.6.0", + "jest": "^30.3.0", "jest-fetch-mock": "^3.0.3", - "jest-matcher-utils": "^27.4.6", + "jest-matcher-utils": "^30.3.0", + "nodemon": "^3.1.11", "prettier": "^2.5.1", - "supertest": "^6.2.1" + "supertest": "^7.0.0" } }, "node_modules/@apollo/cache-control-types": { @@ -77,86 +79,54 @@ } }, "node_modules/@apollo/server": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.3.tgz", - "integrity": "sha512-U56Sx/UmzR3Es344hQ/Ptf2EJrH+kV4ZPoLmgGjWoiwf2wYQ/pRSvkSXgjOvoyE34wSa8Gh7f92ljfLfY+6q1w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.5.0.tgz", + "integrity": "sha512-vWtodBOK/SZwBTJzItECOmLfL8E8pn/IdvP7pnxN5g2tny9iW4+9sxdajE798wV1H2+PYp/rRcl/soSHIBKMPw==", "dependencies": { "@apollo/cache-control-types": "^1.0.3", - "@apollo/server-gateway-interface": "^1.1.1", + "@apollo/server-gateway-interface": "^2.0.0", "@apollo/usage-reporting-protobuf": "^4.1.1", - "@apollo/utils.createhash": "^2.0.0", - "@apollo/utils.fetcher": "^2.0.0", - "@apollo/utils.isnodelike": "^2.0.0", - "@apollo/utils.keyvaluecache": "^2.1.0", - "@apollo/utils.logger": "^2.0.0", + "@apollo/utils.createhash": "^3.0.0", + "@apollo/utils.fetcher": "^3.0.0", + "@apollo/utils.isnodelike": "^3.0.0", + "@apollo/utils.keyvaluecache": "^4.0.0", + "@apollo/utils.logger": "^3.0.0", "@apollo/utils.usagereporting": "^2.1.0", - "@apollo/utils.withrequired": "^2.0.0", - "@graphql-tools/schema": "^9.0.0", - "@josephg/resolvable": "^1.0.0", - "@types/express": "^4.17.13", - "@types/express-serve-static-core": "^4.17.30", - "@types/node-fetch": "^2.6.1", + "@apollo/utils.withrequired": "^3.0.0", + "@graphql-tools/schema": "^10.0.0", "async-retry": "^1.2.1", - "body-parser": "^1.20.0", + "body-parser": "^2.2.2", + "content-type": "^1.0.5", "cors": "^2.8.5", - "express": "^4.17.1", + "finalhandler": "^2.1.0", "loglevel": "^1.6.8", - "lru-cache": "^7.10.1", - "negotiator": "^0.6.3", - "node-abort-controller": "^3.1.1", - "node-fetch": "^2.6.7", - "uuid": "^9.0.0", - "whatwg-mimetype": "^3.0.0" + "lru-cache": "^11.1.0", + "negotiator": "^1.0.0", + "uuid": "^11.1.0", + "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">=14.16.0" + "node": ">=20" }, "peerDependencies": { - "graphql": "^16.6.0" + "graphql": "^16.11.0" } }, "node_modules/@apollo/server-gateway-interface": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-1.1.1.tgz", - "integrity": "sha512-pGwCl/po6+rxRmDMFgozKQo2pbsSwE91TpsDBAOgf74CRDPXHHtM88wbwjab0wMMZh95QfR45GGyDIdhY24bkQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz", + "integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==", + "license": "MIT", "dependencies": { "@apollo/usage-reporting-protobuf": "^4.1.1", - "@apollo/utils.fetcher": "^2.0.0", - "@apollo/utils.keyvaluecache": "^2.1.0", - "@apollo/utils.logger": "^2.0.0" + "@apollo/utils.fetcher": "^3.0.0", + "@apollo/utils.keyvaluecache": "^4.0.0", + "@apollo/utils.logger": "^3.0.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, - "node_modules/@apollo/server-gateway-interface/node_modules/@apollo/utils.keyvaluecache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-2.1.1.tgz", - "integrity": "sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==", - "dependencies": { - "@apollo/utils.logger": "^2.0.1", - "lru-cache": "^7.14.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@apollo/server-gateway-interface/node_modules/@apollo/utils.logger": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-2.0.1.tgz", - "integrity": "sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@apollo/server-gateway-interface/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, "node_modules/@apollo/server/node_modules/@apollo/utils.dropunuseddefinitions": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz", @@ -168,26 +138,6 @@ "graphql": "14.x || 15.x || 16.x" } }, - "node_modules/@apollo/server/node_modules/@apollo/utils.keyvaluecache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-2.1.1.tgz", - "integrity": "sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==", - "dependencies": { - "@apollo/utils.logger": "^2.0.1", - "lru-cache": "^7.14.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@apollo/server/node_modules/@apollo/utils.logger": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-2.0.1.tgz", - "integrity": "sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==", - "engines": { - "node": ">=14" - } - }, "node_modules/@apollo/server/node_modules/@apollo/utils.printwithreducedwhitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz", @@ -254,82 +204,125 @@ "graphql": "14.x || 15.x || 16.x" } }, - "node_modules/@apollo/server/node_modules/@graphql-tools/schema": { - "version": "9.0.19", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz", - "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==", + "node_modules/@apollo/server/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", "dependencies": { - "@graphql-tools/merge": "^8.4.1", - "@graphql-tools/utils": "^9.2.1", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@apollo/server/node_modules/@graphql-tools/utils": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", - "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", + "node_modules/@apollo/server/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", "dependencies": { - "@graphql-typed-document-node/core": "^3.1.1", - "tslib": "^2.4.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@apollo/server/node_modules/@types/express-serve-static-core": { - "version": "4.17.36", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz", - "integrity": "sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "node_modules/@apollo/server/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" } }, - "node_modules/@apollo/server/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "node_modules/@apollo/server/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.8" } }, - "node_modules/@apollo/server/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "node_modules/@apollo/server/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/@apollo/server/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/@apollo/server/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@apollo/server/node_modules/value-or-promise": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", - "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", + "node_modules/@apollo/server/node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, "engines": { - "node": ">=12" + "node": ">= 0.10" + } + }, + "node_modules/@apollo/server/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/@apollo/server/node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@apollo/usage-reporting-protobuf": { @@ -365,39 +358,87 @@ } }, "node_modules/@apollo/utils.createhash": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-2.0.1.tgz", - "integrity": "sha512-fQO4/ZOP8LcXWvMNhKiee+2KuKyqIcfHrICA+M4lj/h/Lh1H10ICcUtk6N/chnEo5HXu0yejg64wshdaiFitJg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz", + "integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==", + "license": "MIT", "dependencies": { - "@apollo/utils.isnodelike": "^2.0.1", + "@apollo/utils.isnodelike": "^3.0.0", "sha.js": "^2.4.11" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/@apollo/utils.fetcher": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-2.0.1.tgz", - "integrity": "sha512-jvvon885hEyWXd4H6zpWeN3tl88QcWnHp5gWF5OPF34uhvoR+DFqcNxs9vrRaBBSY3qda3Qe0bdud7tz2zGx1A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz", + "integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A==", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/@apollo/utils.isnodelike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-2.0.1.tgz", - "integrity": "sha512-w41XyepR+jBEuVpoRM715N2ZD0xMD413UiJx8w5xnAZD2ZkSJnMJBoIzauK83kJpSgNuR6ywbV29jG9NmxjK0Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz", + "integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g==", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=16" + } + }, + "node_modules/@apollo/utils.keyvaluecache": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz", + "integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==", + "license": "MIT", + "dependencies": { + "@apollo/utils.logger": "^3.0.0", + "lru-cache": "^11.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@apollo/utils.keyvaluecache/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@apollo/utils.logger": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz", + "integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==", + "license": "MIT", + "engines": { + "node": ">=16" } }, "node_modules/@apollo/utils.withrequired": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-2.0.1.tgz", - "integrity": "sha512-YBDiuAX9i1lLc6GeTy1m7DGLFn/gMnvXqlalOIMjM7DeOgIacEjjfwPqb0M1CQ2v11HhR15d1NmxJoRCfrNqcA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz", + "integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA==", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=16" + } + }, + "node_modules/@as-integrations/express4": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@as-integrations/express4/-/express4-1.1.2.tgz", + "integrity": "sha512-PGeMcwoOKdYnZ4LtsmM7aLNoel3tbK8wKnfyahdRau1qb7wLbuaXB35zg3w34Ov4bm3WJtO3yzd8Bw5jVE+aIQ==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@apollo/server": "^4.0.0 || ^5.0.0", + "express": "^4.0.0" } }, "node_modules/@babel/cli": { @@ -430,48 +471,49 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", - "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", - "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7", - "convert-source-map": "^1.7.0", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -481,16 +523,23 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" @@ -522,23 +571,36 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.7.tgz", @@ -629,14 +691,11 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, "engines": { "node": ">=6.9.0" } @@ -654,34 +713,33 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { @@ -697,9 +755,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "engines": { "node": ">=6.9.0" @@ -772,27 +830,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "engines": { "node": ">=6.9.0" @@ -814,28 +872,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -865,10 +908,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "dev": true, + "dependencies": { + "@babel/types": "^7.29.0" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1231,6 +1277,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -1256,12 +1317,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", - "integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1373,12 +1434,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", - "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1646,16 +1707,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -2017,60 +2077,62 @@ } }, "node_modules/@babel/runtime": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", - "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2082,1734 +2144,1781 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", - "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", "dev": true, + "optional": true, "dependencies": { - "lodash.get": "^4", - "make-error": "^1", - "ts-node": "^9", - "tslib": "^2" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "cosmiconfig": ">=6" + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", "dev": true, + "optional": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", "dev": true, + "optional": true, "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "tslib": "^2.4.0" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "cpu": [ + "ppc64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@graphql-tools/merge": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz", - "integrity": "sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==", - "dependencies": { - "@graphql-tools/utils": "^9.2.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "node": ">=18" } }, - "node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", - "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", - "dependencies": { - "@graphql-typed-document-node/core": "^3.1.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "node_modules/@esbuild/android-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@graphql-tools/merge/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/@graphql-typed-document-node/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", - "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "node_modules/@esbuild/android-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "node_modules/@esbuild/android-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10.10.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", - "dev": true - }, - "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", - "optional": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@jest/console": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.4.6.tgz", - "integrity": "sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", - "slash": "^3.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=18" } }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=18" } }, - "node_modules/@jest/console/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=7.0.0" + "node": ">=18" } }, - "node_modules/@jest/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "cpu": [ + "ia32" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "cpu": [ + "loong64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "cpu": [ + "mips64el" + ], "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@jest/core": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.4.7.tgz", - "integrity": "sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@jest/console": "^27.4.6", - "@jest/reporters": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^27.4.2", - "jest-config": "^27.4.7", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-resolve-dependencies": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "jest-watcher": "^27.4.6", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "cpu": [ + "s390x" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=7.0.0" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=8.6" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@jest/environment": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.4.6.tgz", - "integrity": "sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=18" } }, - "node_modules/@jest/fake-timers": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.6.tgz", - "integrity": "sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A==", + "node_modules/@esbuild/win32-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=18" } }, - "node_modules/@jest/globals": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.4.6.tgz", - "integrity": "sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/types": "^27.4.2", - "expect": "^27.4.6" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@jest/reporters": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.6.tgz", - "integrity": "sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=7.0.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@jest/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/reporters/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@jest/reporters/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "node_modules/@graphql-tools/merge": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.6.tgz", + "integrity": "sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/@graphql-tools/schema": { + "version": "10.0.30", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.30.tgz", + "integrity": "sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@graphql-tools/merge": "^9.1.6", + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@jest/source-map": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz", - "integrity": "sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==", - "dev": true, + "node_modules/@graphql-tools/utils": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.11.0.tgz", + "integrity": "sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==", + "license": "MIT", "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", + "tslib": "^2.4.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@jest/source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@jest/test-result": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.6.tgz", - "integrity": "sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@jest/console": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10.10.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz", - "integrity": "sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "dependencies": { - "@jest/test-result": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-runtime": "^27.4.6" - }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jest/transform": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.4.6.tgz", - "integrity": "sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "optional": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.4.2", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-util": "^27.4.2", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=12" } }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jest/transform/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8.6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@jest/transform/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/@jest/transform/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@jest/console": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", + "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", + "node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=8" } }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@jest/core": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", + "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jest/console": "30.3.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.3.0", + "jest-config": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-resolve-dependencies": "30.3.0", + "jest-runner": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "jest-watcher": "30.3.0", + "pretty-format": "30.3.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@jest/core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@jest/core/node_modules/jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.3.0" }, "engines": { - "node": ">=7.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@jest/core/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@jest/diff-sequences": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", + "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@josephg/resolvable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", - "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==" - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@jest/environment": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", + "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-mock": "30.3.0" }, "engines": { - "node": ">=6.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@jest/expect": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", "dev": true, + "dependencies": { + "expect": "30.3.0", + "jest-snapshot": "30.3.0" + }, "engines": { - "node": ">=6.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/@jest/expect-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", + "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0" + }, "engines": { - "node": ">=6.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "node_modules/@jest/fake-timers": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", + "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", "dev": true, - "peer": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jest/types": "30.3.0", + "@sinonjs/fake-timers": "^15.0.0", + "@types/node": "*", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/babel-plugin-extract-messages": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.13.0.tgz", - "integrity": "sha512-akDiMq+CrF3m4ENA3DlEj+XfskZow6SqvrkOUVIStow5kUqcCBow635W7+YAem2TJNxH+CpVgpGV24osiQx+ZQ==", + "node_modules/@jest/globals": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", + "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", "dev": true, "dependencies": { - "@babel/generator": "^7.11.6", - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.13.0", - "mkdirp": "^1.0.4" + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/types": "30.3.0", + "jest-mock": "30.3.0" }, "engines": { - "node": ">=10.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/cli": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.13.0.tgz", - "integrity": "sha512-hK/7z+hqxT9CSzUQUQEefurbjmZCJldLG9kbSp8mNgJ+XLAv1mWPve79pYCbtMK7M7vbyU4uG0ncnH+pHKFF/w==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.11.6", - "@babel/parser": "^7.11.5", - "@babel/plugin-syntax-jsx": "^7.10.4", - "@babel/runtime": "^7.11.2", - "@babel/types": "^7.11.5", - "@lingui/babel-plugin-extract-messages": "^3.13.0", - "@lingui/conf": "^3.13.0", - "babel-plugin-macros": "^3.0.1", - "bcp-47": "^1.0.7", - "chalk": "^4.1.0", - "chokidar": "3.5.1", - "cli-table": "0.3.6", - "commander": "^6.1.0", - "date-fns": "^2.16.1", - "fs-extra": "^9.0.1", - "fuzzaldrin": "^2.1.0", - "glob": "^7.1.4", - "inquirer": "^7.3.3", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3", - "micromatch": "4.0.2", - "mkdirp": "^1.0.4", - "node-gettext": "^3.0.0", - "normalize-path": "^3.0.0", - "ora": "^5.1.0", - "papaparse": "^5.3.0", - "pkg-up": "^3.1.0", - "plurals-cldr": "^1.0.4", - "pofile": "^1.1.0", - "pseudolocale": "^1.1.0", - "ramda": "^0.27.1" - }, - "bin": { - "lingui": "lingui.js" + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" }, "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "babel-plugin-macros": "2 || 3", - "typescript": "2 || 3 || 4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@jest/reporters": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", + "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@lingui/cli/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/@lingui/cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=10" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lingui/cli/node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "node_modules/@jest/reporters/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">= 8.10.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" }, "optionalDependencies": { - "fsevents": "~2.3.1" + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/@lingui/cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lingui/cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@lingui/cli/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lingui/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@jest/reporters/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/@lingui/cli/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, + "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@lingui/cli/node_modules/make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==", - "dev": true - }, - "node_modules/@lingui/cli/node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "node_modules/@jest/snapshot-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", + "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" }, "engines": { - "node": ">=8.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/conf": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.13.0.tgz", - "integrity": "sha512-1vl7NEZWMuiM2JCqnvlGmoyqlwB4isSEZrzvKWGAGMRLxMuuKR6PrH1Khgl4x2WRLZxfEysXTe6YR08Ra68irQ==", + "node_modules/@jest/test-result": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", + "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.11.2", - "@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "jest-validate": "^26.5.2", - "lodash.get": "^4.4.2" + "@jest/console": "30.3.0", + "@jest/types": "30.3.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" }, "engines": { - "node": ">=10.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/conf/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@jest/test-sequencer": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", + "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jest/test-result": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/conf/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@jest/test-sequencer/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/@lingui/conf/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" + "node_modules/@jest/transform": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", + "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" }, "engines": { - "node": ">=7.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/conf/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "node_modules/@lingui/conf/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@jest/transform/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/@lingui/conf/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@jest/types": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/core": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.13.0.tgz", - "integrity": "sha512-UDmI8UL59rLmQDDjBK8JFMX0+i3+pncl3fWG+tD2cXNJkN+MEBrhECTQ2lsM1tCk09AfiATglPPXm1e0tLxxOw==", + "node_modules/@jest/types/node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.11.2", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3" + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": ">=10.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@lingui/core/node_modules/make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" + "node_modules/@jest/types/node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "dev": true }, - "node_modules/@lingui/loader": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-3.13.0.tgz", - "integrity": "sha512-MuLwM6vVMYH/ilCGunNGIPTvZU9gtjaohU54/bDCRbDJ5teN46UKqFTYqP6TwVWa+AF6hRMcgUprBcQJi7Iw8Q==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/cli": "^3.13.0", - "@lingui/conf": "^3.13.0", - "loader-utils": "^2.0.0", - "ramda": "^0.27.1" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@lingui/macro": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.13.0.tgz", - "integrity": "sha512-TmwAiFnxtutDEKp7KFtUmq5vIfv56zn0FV0ZgrISUcW1liVlRyqW6YnQ7cv4AzsPnkBhO2+O2YVoHY1r5owMvA==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.13.0", - "ramda": "^0.27.1" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "babel-plugin-macros": "2 || 3" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, - "optional": true + "engines": { + "node": ">=6.0.0" + } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@lingui/babel-plugin-extract-messages": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-5.4.1.tgz", + "integrity": "sha512-sjkVaLyuK3ZW62mv5gU6pOdl3ZpwDReeSaNodJuf9LssbMIQPa5WOirTnMeBaalrQ8BA2srrRzQAWgsXPQVdXA==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=20.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@lingui/babel-plugin-lingui-macro": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-lingui-macro/-/babel-plugin-lingui-macro-5.4.1.tgz", + "integrity": "sha512-9IO+PDvdneY8OCI8zvI1oDXpzryTMtyRv7uq9O0U1mFCvIPVd5dWQKQDu/CpgpYAc2+JG/izn5PNl9xzPc6ckw==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@babel/core": "^7.20.12", + "@babel/runtime": "^7.20.13", + "@babel/types": "^7.20.7", + "@lingui/conf": "5.4.1", + "@lingui/core": "5.4.1", + "@lingui/message-utils": "5.4.1" }, "engines": { - "node": ">= 8" + "node": ">=20.0.0" + }, + "peerDependencies": { + "babel-plugin-macros": "2 || 3" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "node_modules/@lingui/babel-plugin-lingui-macro/node_modules/@lingui/core": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.4.1.tgz", + "integrity": "sha512-4FeIh56PH5vziPg2BYo4XYWWOHE4XaY/XR8Jakwn0/qwtLpydWMNVpZOpGWi7nfPZtcLaJLmZKup6UNxEl1Pfw==", + "dev": true, "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "5.4.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@lingui/babel-plugin-lingui-macro": "5.4.1", + "babel-plugin-macros": "2 || 3" + }, + "peerDependenciesMeta": { + "@lingui/babel-plugin-lingui-macro": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + "node_modules/@lingui/babel-plugin-lingui-macro/node_modules/@lingui/message-utils": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.4.1.tgz", + "integrity": "sha512-hXfL90fFBoKp5YgLaWo3HbJS/7q+WlWs7VwVbUxl4pa+YladqNZf08JoDeBUDtlEVx5a3bNUSACXHs2FZo12aw==", + "dev": true, + "dependencies": { + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" + }, + "engines": { + "node": ">=20.0.0" + } }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + "node_modules/@lingui/cli": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-5.4.1.tgz", + "integrity": "sha512-UAKA9Iz4zMDJS7fzWMZ4hzQWontrTBnI5XCsPm7ttB0Ed0F4Pwph/Vu7pg4bJdiYr4d6nqEpRWd9aTxcC15/IA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.0", + "@babel/generator": "^7.21.1", + "@babel/parser": "^7.22.0", + "@babel/runtime": "^7.21.0", + "@babel/types": "^7.21.2", + "@lingui/babel-plugin-extract-messages": "5.4.1", + "@lingui/babel-plugin-lingui-macro": "5.4.1", + "@lingui/conf": "5.4.1", + "@lingui/core": "5.4.1", + "@lingui/format-po": "5.4.1", + "@lingui/message-utils": "5.4.1", + "chokidar": "3.5.1", + "cli-table": "^0.3.11", + "commander": "^10.0.0", + "convert-source-map": "^2.0.0", + "date-fns": "^3.6.0", + "esbuild": "^0.25.1", + "glob": "^11.0.0", + "micromatch": "^4.0.7", + "normalize-path": "^3.0.0", + "ora": "^5.1.0", + "picocolors": "^1.1.1", + "pofile": "^1.1.4", + "pseudolocale": "^2.0.0", + "source-map": "^0.8.0-beta.0" + }, + "bin": { + "lingui": "dist/lingui.js" + }, + "engines": { + "node": ">=20.0.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "node_modules/@lingui/cli/node_modules/@lingui/core": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.4.1.tgz", + "integrity": "sha512-4FeIh56PH5vziPg2BYo4XYWWOHE4XaY/XR8Jakwn0/qwtLpydWMNVpZOpGWi7nfPZtcLaJLmZKup6UNxEl1Pfw==", "dev": true, "dependencies": { - "type-detect": "4.0.8" + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "5.4.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@lingui/babel-plugin-lingui-macro": "5.4.1", + "babel-plugin-macros": "2 || 3" + }, + "peerDependenciesMeta": { + "@lingui/babel-plugin-lingui-macro": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "node_modules/@lingui/cli/node_modules/@lingui/message-utils": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.4.1.tgz", + "integrity": "sha512-hXfL90fFBoKp5YgLaWo3HbJS/7q+WlWs7VwVbUxl4pa+YladqNZf08JoDeBUDtlEVx5a3bNUSACXHs2FZo12aw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/@lingui/cli/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "engines": { - "node": ">= 6" + "node": "18 || 20 || >=22" } }, - "node_modules/@types/babel__core": { - "version": "7.1.17", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.17.tgz", - "integrity": "sha512-6zzkezS9QEIL8yCBvXWxPTJPNuMeECJVxSOhxNY/jfq9LxOTHivaYTqr37n9LknWWRTIkzqH2UilS5QFvfa90A==", + "node_modules/@lingui/cli/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", - "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "node_modules/@lingui/cli/node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" } }, - "node_modules/@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "node_modules/@lingui/cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "engines": { + "node": ">=14" } }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" - } + "node_modules/@lingui/cli/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, - "node_modules/@types/eslint": { - "version": "8.21.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.2.tgz", - "integrity": "sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw==", + "node_modules/@lingui/cli/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, - "peer": true, "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "node_modules/@lingui/cli/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "peer": true, "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "node_modules/@lingui/cli/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", "dev": true, - "peer": true - }, - "node_modules/@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "node_modules/@lingui/cli/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, "dependencies": { - "@types/node": "*" + "punycode": "^2.1.0" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "node_modules/@lingui/cli/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", "dev": true }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "node_modules/@lingui/cli/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "dependencies": { - "@types/istanbul-lib-coverage": "*" + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "node_modules/@lingui/conf": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-5.4.1.tgz", + "integrity": "sha512-aDkj/bMSr/mCL8Nr1TS52v0GLCuVa4YqtRz+WvUCFZw/ovVInX0hKq1TClx/bSlhu60FzB/CbclxFMBw8aLVUg==", "dev": true, "dependencies": { - "@types/istanbul-lib-report": "*" + "@babel/runtime": "^7.20.13", + "cosmiconfig": "^8.0.0", + "jest-validate": "^29.4.3", + "jiti": "^1.17.1", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "node_modules/@lingui/conf/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "node_modules/@types/node": { - "version": "16.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", - "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" - }, - "node_modules/@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "node_modules/@lingui/conf/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz", - "integrity": "sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "node_modules/@lingui/conf/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "dependencies": { - "@types/mime": "^1", - "@types/node": "*" + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "node_modules/@lingui/core": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-4.13.0.tgz", + "integrity": "sha512-kRqQWeEVoqNrDtEdyHPWGsAHRStN8ObYc5a1gdyuBhoj1zaoUS/DMK5C7B1ZeTtj6rCCmZRs6d2tN12hsZ2zJA==", "dependencies": { - "@types/mime": "^1", - "@types/node": "*" + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "4.13.0", + "unraw": "^3.0.0" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "node_modules/@lingui/format-po": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-5.4.1.tgz", + "integrity": "sha512-IBVq3RRLNEVRzNZcdEw0qpM5NKX4e9wDmvJMorkR2OYrgTbhWx5gDYhXpEZ9yqtuEVhILMdriVNjAAUnDAJibA==", "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "@lingui/conf": "5.4.1", + "@lingui/message-utils": "5.4.1", + "date-fns": "^3.6.0", + "pofile": "^1.1.4" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", - "dev": true - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.1.tgz", - "integrity": "sha512-NtoPsqmcSsWty0mcL5nTZXMf7Ei0Xr2MT8jWjXMVgRK0/1qeQ2jZzLFUh4QtyJ4+/lPUyMw5cSfeeME+Zrtp9Q==", + "node_modules/@lingui/format-po/node_modules/@lingui/message-utils": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.4.1.tgz", + "integrity": "sha512-hXfL90fFBoKp5YgLaWo3HbJS/7q+WlWs7VwVbUxl4pa+YladqNZf08JoDeBUDtlEVx5a3bNUSACXHs2FZo12aw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.31.1", - "@typescript-eslint/types": "4.31.1", - "@typescript-eslint/typescript-estree": "4.31.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" + "node": ">=20.0.0" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/@lingui/macro": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-4.13.0.tgz", + "integrity": "sha512-OjhaWOWbTCXHOOHNaGI0shMP3qrPjNZ19tpEx/iStAmJq64fkevx/HbDPI0uuqLX8v1NFWG/SzBMIQzJb5YOvA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^2.0.0" + "@babel/runtime": "^7.20.13", + "@babel/types": "^7.20.7", + "@lingui/conf": "4.13.0", + "@lingui/core": "4.13.0", + "@lingui/message-utils": "4.13.0" }, "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "node": ">=16.0.0" }, "peerDependencies": { - "eslint": ">=5" + "@lingui/react": "^4.0.0", + "babel-plugin-macros": "2 || 3" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.31.1.tgz", - "integrity": "sha512-N1Uhn6SqNtU2XpFSkD4oA+F0PfKdWHyr4bTX0xTj8NRx1314gBDRL1LUuZd5+L3oP+wo6hCbZpaa1in6SwMcVQ==", + "node_modules/@lingui/macro/node_modules/@lingui/conf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-4.13.0.tgz", + "integrity": "sha512-7NSinlzgSMKBezLsSM7DMwr0IpTHKr8nuSDpTZpI79+BhW+Xq38jPRQqMXdzItW8Cl/Lsdr3Y3MnYJIl8tADsQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.31.1", - "@typescript-eslint/visitor-keys": "4.31.1" + "@babel/runtime": "^7.20.13", + "chalk": "^4.1.0", + "cosmiconfig": "^8.0.0", + "jest-validate": "^29.4.3", + "jiti": "^1.17.1", + "lodash.get": "^4.4.2" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.1.tgz", - "integrity": "sha512-kixltt51ZJGKENNW88IY5MYqTBA8FR0Md8QdGbJD2pKZ+D5IvxjTYDNtJPDxFBiXmka2aJsITdB1BtO1fsgmsQ==", - "dev": true, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } + "node_modules/@lingui/macro/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.1.tgz", - "integrity": "sha512-EGHkbsUvjFrvRnusk6yFGqrqMBTue5E5ROnS5puj3laGQPasVUgwhrxfcgkdHNFECHAewpvELE1Gjv0XO3mdWg==", + "node_modules/@lingui/macro/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.31.1", - "@typescript-eslint/visitor-keys": "4.31.1", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" }, "peerDependenciesMeta": { "typescript": { @@ -3817,2038 +3926,1125 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@lingui/macro/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "argparse": "^2.0.1" }, "bin": { - "semver": "bin/semver.js" + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@lingui/message-utils": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.13.0.tgz", + "integrity": "sha512-tI/WBVZym+APwqk0O3xTaF0k+RQIv5E4PqGHdXqwbofycHly2C+izH+hg6UeNctc6jd19GRwqu/4ga9knkdAlQ==", + "dependencies": { + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.1.tgz", - "integrity": "sha512-PCncP8hEqKw6SOJY+3St4LVtoZpPPn+Zlpm7KW5xnviMhdqcsBty4Lsg4J/VECpJjw1CkROaZhH4B8M1OfnXTQ==", + "node_modules/@lingui/react": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/react/-/react-4.13.0.tgz", + "integrity": "sha512-5remR9rVwosiiX/RnEWETHA8cpqQiP7U87OXXMPz67LuyG3XP8RP+ic75rVn284DHLHgpjDbauz7vYIz855ZoQ==", "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/types": "4.31.1", - "eslint-visitor-keys": "^2.0.0" + "@babel/runtime": "^7.20.13", + "@lingui/core": "4.13.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": ">=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "peer": true, + "node_modules/@messageformat/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "moo": "^0.5.1" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", "dev": true, - "peer": true, + "optional": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", "dev": true, - "peer": true + "optional": true }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "peer": true, "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "peer": true, - "dependencies": { - "@xtuc/long": "4.2.2" + "engines": { + "node": ">= 8" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "peer": true + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@noble/hashes": "^1.1.5" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "optional": true, + "engines": { + "node": ">=14" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, - "peer": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "type-detect": "4.0.8" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "node_modules/@sinonjs/fake-timers": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", + "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, - "peer": true + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "peer": true - }, - "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "node_modules/accept-language": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/accept-language/-/accept-language-3.0.18.tgz", - "integrity": "sha1-9QJfF79lpGaoRYOMz5jNuHfYM4Q=", "dependencies": { - "bcp47": "^1.1.2", - "stable": "^0.1.6" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@types/babel__generator": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", + "dev": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" + "@babel/types": "^7.0.0" } }, - "node_modules/acorn": { + "node_modules/@types/babel__template": { "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", "dev": true, "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" + "@babel/types": "^7.3.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "@types/istanbul-lib-report": "*" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/node": { + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "dependencies": { - "debug": "4" + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "debug": "^4.4.3" }, "engines": { - "node": ">= 6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, - "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, "peerDependencies": { - "ajv": "^6.9.1" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@typescript-eslint/types": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", "dev": true, "engines": { - "node": ">=6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=4" + "node": "18 || 20 || >=22" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/arango-tools": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/arango-tools/-/arango-tools-0.6.0.tgz", - "integrity": "sha512-2XjPPddz7Vc07JrOyXFYGzXI+QZOAR+I0kiyklKBevWVTTDh1lYRetZh3X1fN+smibgL2DY3jKwHK14aSzVkSw==", - "dependencies": { - "arangojs": "^7.2.0", - "assign-deep": "^1.0.1", - "json-placeholder-replacer": "^1.0.35" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/arango-tools/node_modules/arangojs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-7.8.0.tgz", - "integrity": "sha512-aJFlMKlVr4sIO5GDMuykBVNVxWeZTkWDgYbbl9cIuxVctp8Lhs6dW5fr5MYlwAndnOEyi3bvbrhZIucly2IpWQ==", - "dependencies": { - "@types/node": ">=13.13.4", - "es6-error": "^4.0.1", - "multi-part": "^3.0.0", - "x3-linkedlist": "1.2.0", - "xhr": "^2.4.1" + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, - "node_modules/arango-tools/node_modules/file-type": { - "version": "12.4.2", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", - "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/arango-tools/node_modules/mime-kind": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-3.0.0.tgz", - "integrity": "sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "dev": true, "dependencies": { - "file-type": "^12.1.0", - "mime-types": "^2.1.24" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/arango-tools/node_modules/multi-part": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-3.0.0.tgz", - "integrity": "sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw==", - "dependencies": { - "mime-kind": "^3.0.0", - "multi-part-lite": "^1.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/arangojs": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-8.0.0.tgz", - "integrity": "sha512-VjeWxbS18c65Wu6Pr/d5/kW0Chn6ftIfUAzCmG3wUUe8OrRRalSgL6e1+5iY9apocbECo6wTK3kr2goXfLHB+A==", - "dependencies": { - "@types/node": ">=14", - "multi-part": "^4.0.0", - "path-browserify": "^1.0.1", - "x3-linkedlist": "1.2.0", - "xhr": "^2.4.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "engines": { - "node": ">=14" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" + "@typescript-eslint/types": "8.57.2", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - }, "engines": { - "node": ">= 0.4" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "engines": { - "node": ">=0.10.0" + "url": "https://opencollective.com/eslint" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, - "node_modules/assign-deep": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/assign-deep/-/assign-deep-1.0.1.tgz", - "integrity": "sha512-CSXAX79mibneEYfqLT5FEmkqR5WXF+xDRjgQQuVf6wSCXCYU8/vHttPidNar7wJ5BFmKAo8Wei0rCtzb+M/yeA==", - "dependencies": { - "assign-symbols": "^2.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/assign-symbols": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-2.0.2.tgz", - "integrity": "sha512-9sBQUQZMKFKcO/C3Bo6Rx4CQany0R0UeVcefNGRRdW2vbmaMOhV1sbmlXcQLcD56juLXbSGTBm0GGuvmrAF8pA==", - "engines": { - "node": ">=6" - } + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], "dev": true, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dependencies": { - "retry": "0.13.1" - } + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], "dev": true, - "engines": { - "node": ">= 4.0.0" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", - "dependencies": { - "follow-redirects": "^1.14.7" - } + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/babel-core": { - "version": "7.0.0-bridge.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", - "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], "dev": true, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/babel-jest": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.6.tgz", - "integrity": "sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg==", + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.4.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz", - "integrity": "sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz", - "integrity": "sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.0", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz", - "integrity": "sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.20.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz", - "integrity": "sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "dependencies": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" - } - }, - "node_modules/babel-polyfill/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true - }, - "node_modules/babel-polyfill/node_modules/regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz", - "integrity": "sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^27.4.0", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "node_modules/backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcp-47": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", - "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", - "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/bcp47": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", - "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/body-parser/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/body-parser/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", - "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001286", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", - "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "optional": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "optional": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", - "dev": true, - "dependencies": { - "colors": "1.0.3" - }, - "engines": { - "node": ">= 0.2.0" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } + "optional": true, + "os": [ + "linux" ] }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "dependencies": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/core-js": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/core-js-compat": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.3.tgz", - "integrity": "sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw==", + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "browserslist": "^4.19.1", - "semver": "7.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], "dev": true, - "bin": { - "semver": "bin/semver.js" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], "dev": true, + "optional": true, "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "@napi-rs/wasm-runtime": "^0.2.11" }, "engines": { - "node": ">=10" + "node": ">=14.0.0" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "node-fetch": "2.6.7" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@whatwg-node/promise-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "tslib": "^2.6.3" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, + "node_modules/accept-language": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/accept-language/-/accept-language-3.0.18.tgz", + "integrity": "sha1-9QJfF79lpGaoRYOMz5jNuHfYM4Q=", "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" + "bcp47": "^1.1.2", + "stable": "^0.1.6" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/dataloader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", - "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==" - }, - "node_modules/date-fns": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.23.0.tgz", - "integrity": "sha512-5ycpauovVyAk0kXNZz6ZoB9AYMZB4DObse7P3BPWmyEjXNORTI8EJ6X0uaSAq4sCHzM1uajzrkr6HnsLQpxGXA==", - "dev": true, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "node": ">= 0.6" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/accesscontrol": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/accesscontrol/-/accesscontrol-2.2.1.tgz", + "integrity": "sha512-52EvFk/J9EF+w4mYQoKnOTkEMj01R1U5n2fc1dai6x1xkgOks3DGkx01qQL2cKFxGmE4Tn1krAU3jJA9L1NMkg==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "notation": "^1.3.6" } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", - "dev": true - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "dependencies": { - "clone": "^1.0.2" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">= 0.4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, "engines": { - "node": ">=0.4.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, + "node_modules/arango-tools": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/arango-tools/-/arango-tools-0.6.0.tgz", + "integrity": "sha512-2XjPPddz7Vc07JrOyXFYGzXI+QZOAR+I0kiyklKBevWVTTDh1lYRetZh3X1fN+smibgL2DY3jKwHK14aSzVkSw==", "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "arangojs": "^7.2.0", + "assign-deep": "^1.0.1", + "json-placeholder-replacer": "^1.0.35" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "node_modules/arango-tools/node_modules/arangojs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-7.8.0.tgz", + "integrity": "sha512-aJFlMKlVr4sIO5GDMuykBVNVxWeZTkWDgYbbl9cIuxVctp8Lhs6dW5fr5MYlwAndnOEyi3bvbrhZIucly2IpWQ==", + "dependencies": { + "@types/node": ">=13.13.4", + "es6-error": "^4.0.1", + "multi-part": "^3.0.0", + "x3-linkedlist": "1.2.0", + "xhr": "^2.4.1" + }, "engines": { - "node": ">=0.3.1" + "node": ">=10" } }, - "node_modules/diff-sequences": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", - "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", - "dev": true, + "node_modules/arango-tools/node_modules/file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=8" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, + "node_modules/arango-tools/node_modules/mime-kind": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-3.0.0.tgz", + "integrity": "sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ==", "dependencies": { - "path-type": "^4.0.0" + "file-type": "^12.1.0", + "mime-types": "^2.1.24" }, "engines": { - "node": ">=8" + "node": ">=8.3.0" } }, - "node_modules/doctrine": { + "node_modules/arango-tools/node_modules/multi-part": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, + "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-3.0.0.tgz", + "integrity": "sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw==", "dependencies": { - "esutils": "^2.0.2" + "mime-kind": "^3.0.0", + "multi-part-lite": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=8.3.0" } }, - "node_modules/dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, + "node_modules/arangojs": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-10.2.2.tgz", + "integrity": "sha512-3Xllq5inTGjros0mBP9NFxrIW8Di0ldtFurLdrXy5z4NDVJPyJtnwUiiGrMPY21NuVu53wUDE23YN50jnX4epw==", "dependencies": { - "webidl-conversions": "^5.0.0" + "@types/node": "^20.11.26" }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "peerDependencies": { + "undici": ">=5.21.0" + }, + "peerDependenciesMeta": { + "undici": { + "optional": true + } } }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/dotenv-safe": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv-safe/-/dotenv-safe-8.2.0.tgz", - "integrity": "sha512-uWwWWdUQkSs5a3mySDB22UtNwyEYi0JtEQu+vDzIqr9OjbDdC2Ip13PnSpi/fctqlYmzkxCeabiyCAOROuAIaA==", "dependencies": { - "dotenv": "^8.2.0" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" + "sprintf-js": "~1.0.2" } }, - "node_modules/ee-first": { + "node_modules/array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.17", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.17.tgz", - "integrity": "sha512-zhk1MravPtq/KBhmGB7TLBILmXTgRG9TFSI3qS3DbgyfHzIl72iiTE37r/BHIbPCJJlWIo5rySyxiH4vWhu2ZA==", - "dev": true + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "node_modules/array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "node_modules/array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "peer": true, + "node_modules/assign-deep": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/assign-deep/-/assign-deep-1.0.1.tgz", + "integrity": "sha512-CSXAX79mibneEYfqLT5FEmkqR5WXF+xDRjgQQuVf6wSCXCYU8/vHttPidNar7wJ5BFmKAo8Wei0rCtzb+M/yeA==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "assign-symbols": "^2.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dev": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, + "node_modules/assign-symbols": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-2.0.2.tgz", + "integrity": "sha512-9sBQUQZMKFKcO/C3Bo6Rx4CQany0R0UeVcefNGRRdW2vbmaMOhV1sbmlXcQLcD56juLXbSGTBm0GGuvmrAF8pA==", "engines": { - "node": ">=10.13.0" + "node": ">=6" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" + "retry": "0.13.1" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "possible-typed-array-names": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5857,223 +5053,247 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "node_modules/axios": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/babel-core": { + "version": "7.0.0-bridge.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", + "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", "dev": true, - "peer": true + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/babel-jest": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", + "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "@jest/transform": "30.3.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/babel-jest/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, "engines": { - "node": ">=0.8.0" + "node": ">=12" } }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "node_modules/babel-plugin-jest-hoist": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", + "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", "dev": true, "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" + "@types/babel__core": "^7.20.5" }, "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, "engines": { - "node": ">=4.0" + "node": ">=10", + "npm": ">=6" } }, - "node_modules/escodegen/node_modules/levn": { + "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz", + "integrity": "sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==", "dev": true, "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.0", + "semver": "^6.1.1" }, - "engines": { - "node": ">= 0.8.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz", + "integrity": "sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A==", "dev": true, "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.20.0" }, - "engines": { - "node": ">= 0.8.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz", + "integrity": "sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==", "dev": true, - "engines": { - "node": ">= 0.8.0" + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" } }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "node_modules/babel-polyfill/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true + }, + "node_modules/babel-polyfill/node_modules/regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, "dependencies": { - "prelude-ls": "~1.1.2" + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, - "engines": { - "node": ">= 0.8.0" + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, - "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "node_modules/babel-preset-jest": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", + "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", "dev": true, "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "babel-plugin-jest-hoist": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" } }, - "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, - "node_modules/eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "node_modules/babel-runtime/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -6088,328 +5308,305 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1 || ^5.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } + ] }, - "node_modules/eslint-module-utils": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", - "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "find-up": "^2.1.0" - }, + "node_modules/bcp47": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", + "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4=", "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" + "node": ">=0.10" } }, - "node_modules/eslint-module-utils/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" }, - "node_modules/eslint-module-utils/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-module-utils/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/eslint-module-utils/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "dependencies": { - "p-limit": "^1.1.0" + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=4" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/eslint-module-utils/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/eslint-module-utils/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" + "fill-range": "^7.1.1" }, "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" + "node": ">=8" } }, - "node_modules/eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", - "has": "^1.0.3", - "is-core-module": "^2.8.0", - "is-glob": "^4.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, - "engines": { - "node": ">=4" + "bin": { + "browserslist": "cli.js" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "node-int64": "^0.4.0" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/eslint-plugin-jest": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.7.0.tgz", - "integrity": "sha512-wUxdF2bAZiYSKBclsUMrYHH6WxiBreNjyDxbRv345TIvPeoCEgPNEn3Sa+ZrSqsf1Dl9SqqSREXMHExlMMu1DA==", + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "^4.0.1" - }, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": ">= 4", - "eslint": ">=5" + "node": ">=6" }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" + "node": ">=10" } }, - "node_modules/eslint-plugin-node/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">= 4" + "node": ">= 0.8" } }, - "node_modules/eslint-plugin-promise": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz", - "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==", - "dev": true, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "eslint": "^7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.4" } }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" + "node": ">=6" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/caniuse-lite": { + "version": "1.0.30001675", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001675.tgz", + "integrity": "sha512-/wV1bQwPrkLiQMjaJF5yUMVM/VdRPOCU8QZ+PmG6uW6DvYSrNY1bpwHI/3mOcUosLaJCzYDi5o91IQB51ft6cg==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "node_modules/eslint/node_modules/chalk": { + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -6425,343 +5622,278 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { + "node_modules/chalk/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/eslint/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=10" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/chokidar/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8" + "node": ">=8.10.0" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=8" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/cli-spinners": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", "dev": true, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/cli-table": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "dependencies": { + "colors": "1.0.3" }, "engines": { - "node": ">=4" + "node": ">= 0.2.0" } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=12" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true, "engines": { - "node": ">=4.0" + "node": ">=0.8" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=6" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, + "node_modules/cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "engines": { - "node": ">=4.0" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">= 0.6" + "node": ">=7.0.0" } }, - "node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", "dev": true, - "peer": true, "engines": { - "node": ">=0.8.x" + "node": ">=0.1.90" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, - "engines": { - "node": ">= 0.8.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expect": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz", - "integrity": "sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag==", - "dev": true, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dependencies": { - "@jest/types": "^27.4.2", - "jest-get-type": "^27.4.0", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 0.6" } }, - "node_modules/express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.7", - "range-parser": "~1.2.1", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-request-language": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/express-request-language/-/express-request-language-1.1.15.tgz", - "integrity": "sha512-KiLUdEZCcgwh8qfIvkCrhz1MMAFx/Xj4UcspN4zUxVdp+bp+yFvqUMmlyMHK2nC5JlQV7VK5uFOoS5LrArTL1A==", - "dependencies": { - "accept-language": "^3.0.4", - "bcp47": "^1.1.2" - } - }, - "node_modules/express/node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" + "node": ">= 0.8.0" } }, - "node_modules/express/node_modules/debug": { + "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", @@ -6769,26 +5901,57 @@ "ms": "2.0.0" } }, - "node_modules/express/node_modules/ms": { + "node_modules/compression/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/express/node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/express/node_modules/safe-buffer": { + "node_modules/content-disposition/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", @@ -6805,1217 +5968,1541 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" }, "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, - "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "node_modules/core-js": { + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "browserslist": "^4.22.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" }, "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": ">=8.6" + "node": ">=10" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "dependencies": { + "node-fetch": "2.6.7" + } }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "node_modules/cross-inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/dataloader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", + "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==" + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "dependencies": { - "bser": "2.1.1" + "clone": "^1.0.2" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "escape-string-regexp": "^1.0.5" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "object-keys": "^1.0.12" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.4" } }, - "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" + "node": ">=0.4.0" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "asap": "^2.0.0", + "wrappy": "1" } }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "esutils": "^2.0.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=6.0.0" } }, - "node_modules/flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", "engines": { - "node": ">= 6" + "node": ">=10" } }, - "node_modules/formidable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", - "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==", - "dev": true, + "node_modules/dotenv-safe": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv-safe/-/dotenv-safe-8.2.0.tgz", + "integrity": "sha512-uWwWWdUQkSs5a3mySDB22UtNwyEYi0JtEQu+vDzIqr9OjbDdC2Ip13PnSpi/fctqlYmzkxCeabiyCAOROuAIaA==", "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" + "dotenv": "^8.2.0" } }, - "node_modules/formidable/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dependencies": { - "side-channel": "^1.0.4" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.4" } }, - "node_modules/forwarded": { + "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" + "safe-buffer": "^5.0.1" } }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "node_modules/electron-to-chromium": { + "version": "1.5.49", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz", + "integrity": "sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/fuzzaldrin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", - "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=", + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">= 0.8" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" + "dependencies": { + "is-arrayish": "^0.2.1" } }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { - "node": ">=8.0.0" + "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "es-errors": "^1.3.0" }, "engines": { "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.4" } }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, + "node_modules/esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", "dev": true, - "dependencies": { - "is-glob": "^4.0.1" + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 6" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "peer": true - }, - "node_modules/global": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "dependencies": { - "min-document": "^2.19.0", - "process": "^0.11.10" + "engines": { + "node": ">=6" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, "engines": { - "node": ">= 4" + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" } }, - "node_modules/globby/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=8" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "node_modules/graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + "node": ">=10" } }, - "node_modules/graphql-depth-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/graphql-depth-limit/-/graphql-depth-limit-1.1.0.tgz", - "integrity": "sha512-+3B2BaG8qQ8E18kzk9yiSdAa75i/hnnOwgSeAxVJctGQPvmeiLtqKOYF6HETCyRjiF7Xfsyal0HbLlxCQkgkrw==", - "dependencies": { - "arrify": "^1.0.1" - }, - "engines": { - "node": ">=6.0.0" + "node_modules/eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" }, "peerDependencies": { - "graphql": "*" + "eslint": ">=7.0.0" } }, - "node_modules/graphql-redis-subscriptions": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/graphql-redis-subscriptions/-/graphql-redis-subscriptions-2.6.0.tgz", - "integrity": "sha512-hWAPkCNSFKpEOJBMLAcbpGl/gv+s1Yho2JIwP9MK2RlvqlWiqhAYqjA/HVRK86nkYsRkBwGgqvaNxtV1fAexBQ==", - "optionalDependencies": { - "ioredis": "^5.2.4" + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=12.0.0" }, "peerDependencies": { - "graphql-subscriptions": "^1.0.0 || ^2.0.0" + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" } }, - "node_modules/graphql-redis-subscriptions/node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "optional": true, - "engines": { - "node": ">=0.10" + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" } }, - "node_modules/graphql-redis-subscriptions/node_modules/ioredis": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", - "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", - "optional": true, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", + "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", + "dev": true, "dependencies": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" + "debug": "^3.2.7", + "find-up": "^2.1.0" }, "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" + "node": ">=4" } }, - "node_modules/graphql-relay": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.0.tgz", - "integrity": "sha512-44yBuw2/DLNEiMypbNZBt1yMDbBmyVPVesPywnteGGALiBmdyy1JP8jSg8ClLePg8ZZxk0O4BLhd1a6U/1jDOQ==", - "engines": { - "node": "^12.20.0 || ^14.15.0 || >= 15.9.0" - }, - "peerDependencies": { - "graphql": "^16.2.0" + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/graphql-scalars": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.22.4.tgz", - "integrity": "sha512-ILnv7jq5VKHLUyoaTFX7lgYrjCd6vTee9i8/B+D4zJKJT5TguOl0KkpPEbXHjmeor8AZYrVsrYUHdqRBMX1pjA==", + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, "dependencies": { - "tslib": "^2.5.0" + "locate-path": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "node": ">=4" } }, - "node_modules/graphql-scalars/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/graphql-subscriptions": { + "node_modules/eslint-module-utils/node_modules/locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-2.0.0.tgz", - "integrity": "sha512-s6k2b8mmt9gF9pEfkxsaO1lTxaySfKoEJzEfmwguBbQ//Oq23hIXCfR1hm4kdh5hnR20RdwB+s3BCb+0duHSZA==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, "dependencies": { - "iterall": "^1.3.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" }, - "peerDependencies": { - "graphql": "^15.7.2 || ^16.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/graphql-validation-complexity": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/graphql-validation-complexity/-/graphql-validation-complexity-0.4.2.tgz", - "integrity": "sha512-4tzmN/70a06c2JH5fvISkoLX6oBDpqK22cvr2comge3HZHtBLD3n5Sl6MnQYMVhQqKGlpZWcCgD00MnyKNzYYg==", + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, "dependencies": { - "warning": "^4.0.3" + "p-try": "^1.0.0" }, - "peerDependencies": { - "graphql": ">=0.9.5" + "engines": { + "node": ">=4" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "p-limit": "^1.1.0" }, "engines": { - "node": ">= 0.4.0" + "node": ">=4" } }, - "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=4" } }, - "node_modules/has-flag": { + "node_modules/eslint-module-utils/node_modules/path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true, "engines": { "node": ">=4" } }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], "dependencies": { - "has-symbols": "^1.0.2" + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" }, "engines": { - "node": ">= 0.4" + "node": "^14.18.0 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": ">=8" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "node_modules/eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, "engines": { - "node": ">=8" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "ms": "2.0.0" } }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "dependencies": { - "whatwg-encoding": "^1.0.5" + "esutils": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "node_modules/eslint-plugin-jest": { + "version": "29.15.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.1.tgz", + "integrity": "sha512-6BjyErCQauz3zfJvzLw/kAez2lf4LEpbHLvWBfEcG4EI0ZiRSwjoH2uZulMouU8kRkBH+S0rhqn11IhTvxKgKw==", + "dev": true, "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" + "@typescript-eslint/utils": "^8.0.0" }, "engines": { - "node": ">= 0.6" + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "jest": "*", + "typescript": ">=4.8.4 <7.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" }, "engines": { - "node": ">= 6" + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "type-fest": "^0.20.2" }, "engines": { - "node": ">= 6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "bin": { + "semver": "bin/semver.js" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "engines": { + "node": ">=10" + } }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/eslint-plugin-n/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">= 4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/eslint-plugin-promise": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/import-fresh/node_modules/resolve-from": { + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "find-up": "^4.0.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=0.8.19" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=7.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=4.0" } }, - "node_modules/ioredis": { - "version": "4.28.3", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.3.tgz", - "integrity": "sha512-9JOWVgBnuSxpIgfpjc1OeY1OLmA4t2KOWWURTDRXky+eWO0LZhI33pQNT9gYxANUXfh5p/zYephYni6GPRsksQ==", - "dependencies": { - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", - "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", - "lodash.isarguments": "^3.1.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" + "node": ">=4.0" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/expect": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.10.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/express-request-language": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/express-request-language/-/express-request-language-1.1.15.tgz", + "integrity": "sha512-KiLUdEZCcgwh8qfIvkCrhz1MMAFx/Xj4UcspN4zUxVdp+bp+yFvqUMmlyMHK2nC5JlQV7VK5uFOoS5LrArTL1A==", + "dependencies": { + "accept-language": "^3.0.4", + "bcp47": "^1.1.2" } }, - "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "ms": "2.0.0" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, + "node_modules/express/node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "reusify": "^1.0.4" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "node_modules/is-generator-fn": { + "node_modules/find-cache-dir": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, "engines": { "node": ">=6" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, "engines": { - "node": ">=8" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, "engines": { "node": ">= 0.4" }, @@ -8023,92 +7510,154 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, "engines": { - "node": ">=0.12.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, + "license": "ISC", "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dependencies": { - "isobject": "^3.0.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.9.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8117,31 +7666,31 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dependencies": { - "has-symbols": "^1.0.2" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "engines": { "node": ">=10" @@ -8150,1062 +7699,958 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" + "min-document": "^2.19.0", + "process": "^0.11.10" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/graphql": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", + "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, + "node_modules/graphql-depth-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/graphql-depth-limit/-/graphql-depth-limit-1.1.0.tgz", + "integrity": "sha512-+3B2BaG8qQ8E18kzk9yiSdAa75i/hnnOwgSeAxVJctGQPvmeiLtqKOYF6HETCyRjiF7Xfsyal0HbLlxCQkgkrw==", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "arrify": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" + }, + "peerDependencies": { + "graphql": "*" } }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/graphql-redis-subscriptions": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/graphql-redis-subscriptions/-/graphql-redis-subscriptions-2.6.0.tgz", + "integrity": "sha512-hWAPkCNSFKpEOJBMLAcbpGl/gv+s1Yho2JIwP9MK2RlvqlWiqhAYqjA/HVRK86nkYsRkBwGgqvaNxtV1fAexBQ==", + "optionalDependencies": { + "ioredis": "^5.2.4" + }, + "peerDependencies": { + "graphql-subscriptions": "^1.0.0 || ^2.0.0" } }, - "node_modules/istanbul-reports": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", - "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, + "node_modules/graphql-redis-subscriptions/node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "optional": true, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/iterall": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", - "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" - }, - "node_modules/jest": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz", - "integrity": "sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==", - "dev": true, + "node_modules/graphql-redis-subscriptions/node_modules/ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "optional": true, "dependencies": { - "@jest/core": "^27.4.7", - "import-local": "^3.0.2", - "jest-cli": "^27.4.7" - }, - "bin": { - "jest": "bin/jest.js" + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": ">=12.22.0" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" } }, - "node_modules/jest-changed-files": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz", - "integrity": "sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, + "node_modules/graphql-relay": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.2.tgz", + "integrity": "sha512-abybva1hmlNt7Y9pMpAzHuFnM2Mme/a2Usd8S4X27fNteLGRAECMYfhmsrpZFvGn3BhmBZugMXYW/Mesv3P1Kw==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.20.0 || ^14.15.0 || >= 15.9.0" + }, + "peerDependencies": { + "graphql": "^16.2.0" } }, - "node_modules/jest-circus": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.6.tgz", - "integrity": "sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ==", - "dev": true, + "node_modules/graphql-scalars": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.25.0.tgz", + "integrity": "sha512-b0xyXZeRFkne4Eq7NAnL400gStGqG/Sx9VqX0A05nHyEbv57UJnWKsjNnrpVqv5e/8N1MUxkt0wwcRXbiyKcFg==", + "license": "MIT", "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" + "tslib": "^2.5.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/graphql-subscriptions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-2.0.0.tgz", + "integrity": "sha512-s6k2b8mmt9gF9pEfkxsaO1lTxaySfKoEJzEfmwguBbQ//Oq23hIXCfR1hm4kdh5hnR20RdwB+s3BCb+0duHSZA==", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "iterall": "^1.3.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "graphql": "^15.7.2 || ^16.0.0" } }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/graphql-validation-complexity": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/graphql-validation-complexity/-/graphql-validation-complexity-0.4.2.tgz", + "integrity": "sha512-4tzmN/70a06c2JH5fvISkoLX6oBDpqK22cvr2comge3HZHtBLD3n5Sl6MnQYMVhQqKGlpZWcCgD00MnyKNzYYg==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "warning": "^4.0.3" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "graphql": ">=0.9.5" } }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "function-bind": "^1.1.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4.0" } }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", "dev": true, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-circus/node_modules/slash": { + "node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "has-flag": "^4.0.0" + "es-define-property": "^1.0.0" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.7.tgz", - "integrity": "sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw==", - "dev": true, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "@jest/core": "^27.4.7", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "jest-config": "^27.4.7", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" + "has-symbols": "^1.0.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { - "color-convert": "^2.0.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/jest-cli/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "parse-passwd": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { - "color-name": "~1.1.4" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/jest-cli/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/jest-config": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.4.7.tgz", - "integrity": "sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.4.6", - "@jest/types": "^27.4.2", - "babel-jest": "^27.4.6", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-circus": "^27.4.6", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-jasmine2": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", - "slash": "^3.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "bin": { + "import-local-fixture": "fixtures/cli.js" }, - "peerDependencies": { - "ts-node": ">=9.0.0" + "engines": { + "node": ">=8" }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "find-up": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-config/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.19" } }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/ioredis": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.3.tgz", + "integrity": "sha512-9JOWVgBnuSxpIgfpjc1OeY1OLmA4t2KOWWURTDRXky+eWO0LZhI33pQNT9gYxANUXfh5p/zYephYni6GPRsksQ==", + "dependencies": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "lodash.isarguments": "^3.1.0", + "p-map": "^2.1.0", + "redis-commands": "1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, "engines": { - "node": ">=8" + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" } }, - "node_modules/jest-config/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 0.10" } }, - "node_modules/jest-config/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "has-bigints": "^1.0.1" }, - "engines": { - "node": ">=8.6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-config/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz", - "integrity": "sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==", + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.4.0", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" + "builtin-modules": "^3.3.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/jest-docblock": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz", - "integrity": "sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==", + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=6" } }, - "node_modules/jest-each": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.4.6.tgz", - "integrity": "sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6" + "is-extglob": "^2.1.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=0.12.0" } }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/jest-environment-jsdom": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz", - "integrity": "sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2", - "jsdom": "^16.6.0" + "isobject": "^3.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-environment-node": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.6.tgz", - "integrity": "sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ==", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-fetch-mock": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", - "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", "dev": true, - "dependencies": { - "cross-fetch": "^3.0.4", - "promise-polyfill": "^8.1.3" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-haste-map": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.6.tgz", - "integrity": "sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "dependencies": { - "@jest/types": "^27.4.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^27.4.0", - "jest-serializer": "^27.4.0", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "micromatch": "^4.0.4", - "walker": "^1.0.7" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-haste-map/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz", - "integrity": "sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", - "throat": "^6.0.1" + "node": ">= 0.4" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-jasmine2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "which-typed-array": "^1.1.16" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-jasmine2/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-jasmine2/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "node_modules/jest-jasmine2/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-jasmine2/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", "dependencies": { - "has-flag": "^4.0.0" - }, + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-leak-detector": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz", - "integrity": "sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "dependencies": { - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" } }, - "node_modules/jest-matcher-utils": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz", - "integrity": "sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA==", + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "semver": "^7.5.3" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { + "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -9217,208 +8662,284 @@ "node": ">=8" } }, - "node_modules/jest-message-util": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz", - "integrity": "sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA==", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.4.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=10" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", + "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/core": "30.3.0", + "@jest/types": "30.3.0", + "import-local": "^3.2.0", + "jest-cli": "30.3.0" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=7.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-changed-files": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", + "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", "dev": true, + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.3.0", + "p-limit": "^3.1.0" + }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-message-util/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/jest-changed-files/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=8.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/jest-circus": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", + "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", "dev": true, + "dependencies": { + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "p-limit": "^3.1.0", + "pretty-format": "30.3.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-circus/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-mock": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.6.tgz", - "integrity": "sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw==", + "node_modules/jest-circus/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@types/node": "*" - }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=8" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, + "node_modules/jest-cli": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", + "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", + "dev": true, + "dependencies": { + "@jest/core": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">=6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "jest-resolve": "*" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "jest-resolve": { + "node-notifier": { "optional": true } } }, - "node_modules/jest-regex-util": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz", - "integrity": "sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==", + "node_modules/jest-cli/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-resolve": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.6.tgz", - "integrity": "sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw==", + "node_modules/jest-cli/node_modules/jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "dependencies": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.3.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-resolve-dependencies": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz", - "integrity": "sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "jest-regex-util": "^27.4.0", - "jest-snapshot": "^27.4.6" + "node_modules/jest-config": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", + "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.3.0", + "@jest/types": "30.3.0", + "babel-jest": "30.3.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.3.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-runner": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "parse-json": "^5.2.0", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "balanced-match": "^1.0.0" } }, - "node_modules/jest-resolve/node_modules/camelcase": { + "node_modules/jest-config/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", @@ -9430,768 +8951,804 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=10" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-config/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jest-resolve/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", + "node_modules/jest-config/node_modules/jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "^27.4.6" + "pretty-format": "30.3.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, - "node_modules/jest-runner": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.6.tgz", - "integrity": "sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg==", - "dev": true, - "dependencies": { - "@jest/console": "^27.4.6", - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-docblock": "^27.4.0", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-haste-map": "^27.4.6", - "jest-leak-detector": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-config/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-config/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-diff": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.6.tgz", - "integrity": "sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/globals": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "detect-newline": "^3.1.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-each": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", + "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "jest-util": "30.3.0", + "pretty-format": "30.3.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-environment-node": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", + "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-mock": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0" }, "engines": { - "node": ">=7.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-environment-node/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-runtime/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/jest-environment-node/node_modules/jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.3.0" + }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" } }, - "node_modules/jest-serializer": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz", - "integrity": "sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==", + "node_modules/jest-haste-map": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", + "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "dependencies": { + "@jest/types": "30.3.0", "@types/node": "*", - "graceful-fs": "^4.2.4" + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", + "picomatch": "^4.0.3", + "walker": "^1.0.8" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.6.tgz", - "integrity": "sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-haste-map": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", - "natural-compare": "^1.4.0", - "pretty-format": "^27.4.6", - "semver": "^7.3.2" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "optionalDependencies": { + "fsevents": "^2.3.3" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-haste-map/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-leak-detector": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", + "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/get-type": "30.1.0", + "pretty-format": "30.3.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-matcher-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", + "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.3.0", + "pretty-format": "30.3.0" }, "engines": { - "node": ">=7.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/jest-message-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", + "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.3.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "node_modules/jest-snapshot/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/jest-mock": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", + "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-util": "30.3.0" }, "engines": { - "node": ">=10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-util": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz", - "integrity": "sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==", + "node_modules/jest-resolve": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", + "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", "dev": true, "dependencies": { - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.4", - "picomatch": "^2.2.3" + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-resolve-dependencies": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", + "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.3.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-resolve/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-resolve/node_modules/jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.3.0" }, "engines": { - "node": ">=7.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-resolve/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-runner": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", + "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jest/console": "30.3.0", + "@jest/environment": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-leak-detector": "30.3.0", + "jest-message-util": "30.3.0", + "jest-resolve": "30.3.0", + "jest-runtime": "30.3.0", + "jest-util": "30.3.0", + "jest-watcher": "30.3.0", + "jest-worker": "30.3.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "node_modules/jest-runner/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "leven": "^3.1.0", - "pretty-format": "^26.6.2" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", + "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", + "dev": true, + "dependencies": { + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/globals": "30.3.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate/node_modules/@types/yargs": { - "version": "15.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", - "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "balanced-match": "^1.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=8" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "node_modules/jest-runtime/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-validate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-validate/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-runtime/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-validate/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "node_modules/jest-snapshot": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", + "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "pretty-format": "30.3.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">= 10.14.2" + "node": ">=10" } }, - "node_modules/jest-validate/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "node_modules/jest-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", + "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" }, "engines": { - "node": ">= 10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/jest-watcher": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.6.tgz", - "integrity": "sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw==", + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-util": "^27.4.2", - "string-length": "^4.0.1" + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-validate/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=7.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-watcher": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", + "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.3.0", + "string-length": "^4.0.2" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-worker": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", - "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", + "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", "dev": true, "dependencies": { "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.3.0", "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "supports-color": "^8.1.1" }, "engines": { - "node": ">= 10.13.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-worker/node_modules/has-flag": { @@ -10218,15 +9775,29 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-sha256": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz", + "integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "dependencies": { "argparse": "^1.0.7", @@ -10236,74 +9807,16 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-parse-even-better-errors": { @@ -10345,27 +9858,21 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "dependencies": { "jws": "^3.2.2", - "lodash": "^4.17.21", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">=12", @@ -10387,21 +9894,21 @@ } }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -10414,15 +9921,6 @@ "node": ">=0.10.0" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -10451,30 +9949,6 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, - "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -10487,17 +9961,6 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -10520,28 +9983,57 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -10558,76 +10050,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/loglevel": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", @@ -10689,12 +10111,6 @@ "semver": "bin/semver" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/make-plural": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.1.0.tgz", @@ -10709,18 +10125,31 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -10728,20 +10157,6 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -10751,22 +10166,24 @@ } }, "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">=8.6" } }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -10782,18 +10199,6 @@ "node": ">= 0.6" } }, - "node_modules/mime-kind": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-4.0.0.tgz", - "integrity": "sha512-qQvglvSpS5mABi30beNFd+uHKtKkxD3dxAmhi2e589XKx+WfVqhg5i5P5LBcVgwwv3BiDpNMBWrHqU+JexW4aA==", - "dependencies": { - "file-type": "^16.5.4", - "mime-types": "^2.1.24" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", @@ -10815,17 +10220,17 @@ } }, "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.1.tgz", + "integrity": "sha512-8lqe85PkqQJzIcs2iD7xW/WSxcncC3/DPVbTOafKNJDIMXwGfwXS350mH4SJslomntN2iYtFBuC0yNO3CEap6g==", "dependencies": { "dom-walk": "^0.1.0" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -10840,16 +10245,14 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" } }, "node_modules/moment": { @@ -10860,23 +10263,16 @@ "node": "*" } }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/multi-part": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-4.0.0.tgz", - "integrity": "sha512-YT/CS0PAe62kT8EoQXcQj8yIcSu18HhYv0s6ShdAFsoFly3oV5QaxODnkj0u7zH0/RFyH47cdcMVpcGXliEFVA==", - "dependencies": { - "mime-kind": "^4.0.0", - "multi-part-lite": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/multi-part-lite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/multi-part-lite/-/multi-part-lite-1.0.0.tgz", @@ -10885,21 +10281,30 @@ "node": ">=8.3.0" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } }, "node_modules/nats": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-2.7.0.tgz", - "integrity": "sha512-KZ6kDoDsFZq8aPFtH72DzvRKzEk4fzRImb0A8vkHi/KQzlt4VXKLaRLdkAnIUBp08+dEfb47LPDRB4JTJqhuZQ==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.18.0.tgz", + "integrity": "sha512-zZF004ejzf67Za0Tva+xphxoxBMNc5IMLqbZ7Ho0j9TMuisjpo+qCd1EktXRCLNxmrZ8O6Tbm1dBsZYNF6yR1A==", "dependencies": { - "nkeys.js": "^1.0.0-9" + "nkeys.js": "1.0.5" }, "engines": { - "node": ">= 10.0.0" + "node": ">= 14.0.0" } }, "node_modules/natural-compare": { @@ -10909,42 +10314,25 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, "node_modules/nkeys.js": { - "version": "1.0.0-9", - "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.0.0-9.tgz", - "integrity": "sha512-m9O0NQT+3rUe1om6MWpxV77EuHql/LdorDH+FYQkoeARcM2V0sQ89kM36fArWaHWq/25EmNmQUW0MhLTcbqW1A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.0.5.tgz", + "integrity": "sha512-u25YnRPHiGVsNzwyHnn+PT90sgAhnS8jUJ1nxmkHMFYCJ6+Ic0lv291w7uhRBpJVJ3PH2GWbYqA151lGCRrB5g==", "dependencies": { - "@types/node": "^14.0.26", - "tweetnacl": "^1.0.3" + "tweetnacl": "1.0.3" }, "engines": { "node": ">=10.0.0" } }, - "node_modules/nkeys.js/node_modules/@types/node": { - "version": "14.18.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.10.tgz", - "integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ==" - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" - }, "node_modules/node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -11002,27 +10390,60 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/node-gettext": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", - "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", - "dev": true, - "dependencies": { - "lodash.get": "^4.4.2" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, "node_modules/node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -11032,13 +10453,19 @@ "node": ">=0.10.0" } }, + "node_modules/notation": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/notation/-/notation-1.3.6.tgz", + "integrity": "sha512-DIuJmrP/Gg1DcXKaApsqcjsJD6jEccqKSfmU3BUx/f1GHsMiTJh70cERwYc64tOmTRTARCeMwkqNNzjh3AHhiw==", + "license": "MIT" + }, "node_modules/notifications-node-client": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/notifications-node-client/-/notifications-node-client-7.0.3.tgz", - "integrity": "sha512-uLuABa7ZK2XIP6aahHbtiCP4noyNIGclJ2idPh7cojnk2retajkpo0AZctSsDP18SUB0kUmHbBxOOJm2Gkb60g==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/notifications-node-client/-/notifications-node-client-8.2.1.tgz", + "integrity": "sha512-wyZh/NbjN8S2uQX18utYtCyC726BBaGeTc4HeUpdhZv5sYKuaQY94N31v9syh8SzVgehyMzW37y08EePmi+k3Q==", "dependencies": { - "axios": "^0.25.0", - "jsonwebtoken": "^9.0.0" + "axios": "^1.7.2", + "jsonwebtoken": "^9.0.2" }, "engines": { "node": ">=14.17.3", @@ -11057,12 +10484,6 @@ "node": ">=8" } }, - "node_modules/nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -11072,9 +10493,13 @@ } }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11141,9 +10566,10 @@ } }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -11151,6 +10577,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -11176,9 +10610,9 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -11186,7 +10620,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -11215,85 +10649,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -11338,11 +10693,12 @@ "node": ">=6" } }, - "node_modules/papaparse": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz", - "integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==", - "dev": true + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", @@ -11388,25 +10744,15 @@ "node": ">=0.10.0" } }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -11440,10 +10786,37 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" }, "node_modules/path-type": { "version": "4.0.0", @@ -11454,28 +10827,16 @@ "node": ">=8" } }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "engines": { "node": ">=8.6" @@ -11494,9 +10855,9 @@ } }, "node_modules/pirates": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", - "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "engines": { "node": ">= 6" @@ -11560,76 +10921,21 @@ "node": ">=4" } }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } + "node_modules/pofile": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.4.tgz", + "integrity": "sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g==", + "dev": true }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/plurals-cldr": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/plurals-cldr/-/plurals-cldr-1.0.4.tgz", - "integrity": "sha512-4nLXqtel7fsCgzi8dvRZvUjfL8SXpP982sKg7b2TgpnR8rDnes06iuQ83trQ/+XdtyMIQkBBbKzX6x97eLfsJQ==", - "dev": true - }, - "node_modules/pofile": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.1.tgz", - "integrity": "sha512-RVAzFGo1Mx9+YukVKSgTLut6r4ZVBW8IVrqGHAPfEsVJN93WSp5HRD6+qNa7av1q/joPKDNJd55m5AJl9GBQGA==", - "dev": true - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11652,19 +10958,37 @@ } }, "node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/pretty-format/node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "dev": true + }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -11685,38 +11009,17 @@ "node": ">= 0.6.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/promise-polyfill": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.0.tgz", "integrity": "sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g==", "dev": true }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -11725,20 +11028,44 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "engines": { + "node": ">=10" + } + }, "node_modules/pseudolocale": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-1.2.0.tgz", - "integrity": "sha512-k0OQFvIlvpRdzR0dPVrrbWX7eE9EaZ6gpZtTlFSDi1Gf9tMy9wiANCNu7JZ0drcKgUri/39a2mBbH0goiQmrmQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-2.1.0.tgz", + "integrity": "sha512-af5fsrRvVwD+MBasBJvuDChT0KDqT0nEwD9NTgbtHJ16FKomWac9ua0z6YVNB4G9x9IOaiGWym62aby6n4tFMA==", "dev": true, "dependencies": { - "commander": "*" + "commander": "^10.0.0" + }, + "bin": { + "pseudolocale": "dist/cli.mjs" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true + "node_modules/pseudolocale/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" }, "node_modules/punycode": { "version": "2.1.1", @@ -11749,10 +11076,29 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, "node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "dependencies": { + "side-channel": "^1.1.0" + }, "engines": { "node": ">=0.6" }, @@ -11760,12 +11106,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11780,91 +11120,60 @@ "type": "patreon", "url": "https://www.patreon.com/feross" }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "peer": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "loose-envify": "^1.1.0" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11874,19 +11183,16 @@ "node": ">= 6" } }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, "dependencies": { - "readable-stream": "^3.6.0" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "node": ">=8.10.0" } }, "node_modules/redis-commands": { @@ -11934,7 +11240,8 @@ "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -11945,18 +11252,6 @@ "@babel/runtime": "^7.8.4" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/regexpu-core": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", @@ -12004,35 +11299,27 @@ "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12059,13 +11346,13 @@ "node": ">=8" } }, - "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/restore-cursor": { @@ -12114,15 +11401,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12146,24 +11424,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -12174,37 +11434,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -12215,23 +11444,24 @@ } }, "node_modules/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "~2.3.0", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" @@ -12241,6 +11471,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -12248,49 +11479,86 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sha.js/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -12325,88 +11593,117 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", - "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/signal-exit": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", + "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", + "dev": true + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "semver": "^7.5.3" }, "engines": { - "node": ">=7.0.0" + "node": ">=10" } }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } }, "node_modules/source-map": { "version": "0.5.7", @@ -12439,7 +11736,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/stable": { @@ -12448,9 +11745,9 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" }, "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" @@ -12459,32 +11756,25 @@ "node": ">=10" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -12493,6 +11783,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, "funding": [ { "type": "github", @@ -12521,15 +11812,30 @@ "node": ">=10" } }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" @@ -12562,12 +11868,26 @@ } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -12603,71 +11923,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/subscriptions-transport-ws": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", - "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "dependencies": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependencies": { - "graphql": "^15.7.2 || ^16.0.0" - } - }, "node_modules/superagent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.0.2.tgz", - "integrity": "sha512-2Kx35bZxLLJMBKtuXezxvD0aZQ7l923VwoCn7EtUx+aFxdG7co7PeRIddfrNtvvMuGaLZXA0mKzX+yWRhjrJ7A==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.1.tgz", + "integrity": "sha512-CcRSdb/P2oUVaEpQ87w9Obsl+E9FruRd6b2b7LdiBtJoyMr2DQt7a89anAfiX/EL59j9b2CbRFvf2S91DhuCww==", "dev": true, "dependencies": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", - "debug": "^4.3.3", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^2.0.1", + "formidable": "^3.5.1", "methods": "^1.1.2", - "mime": "^2.5.0", - "qs": "^6.10.1", - "readable-stream": "^3.6.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/superagent/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" }, "engines": { - "node": ">= 6" + "node": ">=14.18.0" } }, "node_modules/superagent/node_modules/mime": { @@ -12682,25 +11956,10 @@ "node": ">=4.0.0" } }, - "node_modules/superagent/node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/superagent/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -12713,16 +11972,16 @@ } }, "node_modules/supertest": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.2.1.tgz", - "integrity": "sha512-2kBKhfZgnPLmjpzB0n7A2ZnEAWTaLXq4bn3EEVY9w8rUpLyIlSusqKKvWA1Cav7hxXBnXGpxBsSeOHj5wQGe1Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", + "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", "dev": true, "dependencies": { "methods": "^1.1.2", - "superagent": "^7.0.2" + "superagent": "^9.0.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.18.0" } }, "node_modules/supports-color": { @@ -12737,220 +11996,33 @@ "node": ">=4" } }, - "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.6.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", - "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", - "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "peer": true, - "dependencies": { - "jest-worker": "^27.0.6", - "p-limit": "^3.1.0", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, "engines": { - "node": ">= 10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser-webpack-plugin/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, - "peer": true, "dependencies": { - "yocto-queue": "^0.1.0" + "@pkgr/core": "^0.2.9" }, "engines": { - "node": ">=10" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" + "url": "https://opencollective.com/synckit" } }, - "node_modules/terser/node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "peer": true - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -12971,141 +12043,132 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "node_modules/throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "dependencies": { - "os-tmpdir": "~1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=0.6.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "engines": { - "node": ">=4" + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, "engines": { - "node": ">=8.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true }, - "node_modules/token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "node": ">= 0.4" } }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "node_modules/to-buffer/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "is-number": "^7.0.0" }, "engines": { - "node": ">=6" + "node": ">=8.0" } }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">=0.6" } }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" } }, - "node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, - "dependencies": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, "engines": { - "node": ">=10.0.0" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=2.7" + "typescript": ">=4.8.4" } }, "node_modules/tsconfig-paths": { @@ -13142,31 +12205,10 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/tweetnacl": { "version": "1.0.3", @@ -13210,6 +12252,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -13218,27 +12261,33 @@ "node": ">= 0.6" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", "dependencies": { - "is-typedarray": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, + "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -13256,6 +12305,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -13296,23 +12357,84 @@ "node": ">=4" } }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/unraw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz", + "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -13322,16 +12444,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url-slug": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/url-slug/-/url-slug-3.0.2.tgz", @@ -13340,52 +12452,50 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, "node_modules/v8flags": { "version": "3.2.0", @@ -13400,9 +12510,10 @@ } }, "node_modules/validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "version": "13.15.22", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.22.tgz", + "integrity": "sha512-uT/YQjiyLJP7HSrv/dPZqK9L28xf8hsNca01HSz1dfmI0DgMfjopp1rO/z13NeGF1tVystF0Ejx3y4rUKPw+bQ==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -13415,27 +12526,6 @@ "node": ">= 0.8" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -13453,20 +12543,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -13476,130 +12552,11 @@ "defaults": "^1.0.3" } }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", - "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", - "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, "node_modules/whatwg-fetch": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -13631,6 +12588,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -13657,39 +12635,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -13697,35 +12660,28 @@ "dev": true }, "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "node": ">=14" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/x3-linkedlist": { @@ -13743,21 +12699,9 @@ "dependencies": { "global": "~4.4.0", "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } }, "node_modules/xtend": { "version": "4.0.2", @@ -13791,39 +12735,30 @@ } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" } }, "node_modules/yocto-queue": { @@ -13831,7 +12766,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "peer": true, "engines": { "node": ">=10" }, @@ -13848,36 +12782,31 @@ "requires": {} }, "@apollo/server": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.3.tgz", - "integrity": "sha512-U56Sx/UmzR3Es344hQ/Ptf2EJrH+kV4ZPoLmgGjWoiwf2wYQ/pRSvkSXgjOvoyE34wSa8Gh7f92ljfLfY+6q1w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.5.0.tgz", + "integrity": "sha512-vWtodBOK/SZwBTJzItECOmLfL8E8pn/IdvP7pnxN5g2tny9iW4+9sxdajE798wV1H2+PYp/rRcl/soSHIBKMPw==", "requires": { "@apollo/cache-control-types": "^1.0.3", - "@apollo/server-gateway-interface": "^1.1.1", + "@apollo/server-gateway-interface": "^2.0.0", "@apollo/usage-reporting-protobuf": "^4.1.1", - "@apollo/utils.createhash": "^2.0.0", - "@apollo/utils.fetcher": "^2.0.0", - "@apollo/utils.isnodelike": "^2.0.0", - "@apollo/utils.keyvaluecache": "^2.1.0", - "@apollo/utils.logger": "^2.0.0", + "@apollo/utils.createhash": "^3.0.0", + "@apollo/utils.fetcher": "^3.0.0", + "@apollo/utils.isnodelike": "^3.0.0", + "@apollo/utils.keyvaluecache": "^4.0.0", + "@apollo/utils.logger": "^3.0.0", "@apollo/utils.usagereporting": "^2.1.0", - "@apollo/utils.withrequired": "^2.0.0", - "@graphql-tools/schema": "^9.0.0", - "@josephg/resolvable": "^1.0.0", - "@types/express": "^4.17.13", - "@types/express-serve-static-core": "^4.17.30", - "@types/node-fetch": "^2.6.1", + "@apollo/utils.withrequired": "^3.0.0", + "@graphql-tools/schema": "^10.0.0", "async-retry": "^1.2.1", - "body-parser": "^1.20.0", + "body-parser": "^2.2.2", + "content-type": "^1.0.5", "cors": "^2.8.5", - "express": "^4.17.1", + "finalhandler": "^2.1.0", "loglevel": "^1.6.8", - "lru-cache": "^7.10.1", - "negotiator": "^0.6.3", - "node-abort-controller": "^3.1.1", - "node-fetch": "^2.6.7", - "uuid": "^9.0.0", - "whatwg-mimetype": "^3.0.0" + "lru-cache": "^11.1.0", + "negotiator": "^1.0.0", + "uuid": "^11.1.0", + "whatwg-mimetype": "^4.0.0" }, "dependencies": { "@apollo/utils.dropunuseddefinitions": { @@ -13886,20 +12815,6 @@ "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==", "requires": {} }, - "@apollo/utils.keyvaluecache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-2.1.1.tgz", - "integrity": "sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==", - "requires": { - "@apollo/utils.logger": "^2.0.1", - "lru-cache": "^7.14.1" - } - }, - "@apollo/utils.logger": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-2.0.1.tgz", - "integrity": "sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==" - }, "@apollo/utils.printwithreducedwhitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz", @@ -13939,94 +12854,90 @@ "@apollo/utils.stripsensitiveliterals": "^2.0.1" } }, - "@graphql-tools/schema": { - "version": "9.0.19", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz", - "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==", + "body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "requires": { - "@graphql-tools/merge": "^8.4.1", - "@graphql-tools/utils": "^9.2.1", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" } }, - "@graphql-tools/utils": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", - "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", + "iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "requires": { - "@graphql-typed-document-node/core": "^3.1.1", - "tslib": "^2.4.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "@types/express-serve-static-core": { - "version": "4.17.36", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz", - "integrity": "sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==", + "lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==" + }, + "media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==" + }, + "mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==" + }, + "mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "mime-db": "^1.54.0" } }, - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + "raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "requires": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + } }, - "value-or-promise": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", - "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==" + "type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "requires": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + } }, "whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==" } } }, "@apollo/server-gateway-interface": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-1.1.1.tgz", - "integrity": "sha512-pGwCl/po6+rxRmDMFgozKQo2pbsSwE91TpsDBAOgf74CRDPXHHtM88wbwjab0wMMZh95QfR45GGyDIdhY24bkQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz", + "integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==", "requires": { "@apollo/usage-reporting-protobuf": "^4.1.1", - "@apollo/utils.fetcher": "^2.0.0", - "@apollo/utils.keyvaluecache": "^2.1.0", - "@apollo/utils.logger": "^2.0.0" - }, - "dependencies": { - "@apollo/utils.keyvaluecache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-2.1.1.tgz", - "integrity": "sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==", - "requires": { - "@apollo/utils.logger": "^2.0.1", - "lru-cache": "^7.14.1" - } - }, - "@apollo/utils.logger": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-2.0.1.tgz", - "integrity": "sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==" - }, - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" - } + "@apollo/utils.fetcher": "^3.0.0", + "@apollo/utils.keyvaluecache": "^4.0.0", + "@apollo/utils.logger": "^3.0.0" } }, "@apollo/usage-reporting-protobuf": { @@ -14059,28 +12970,55 @@ } }, "@apollo/utils.createhash": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-2.0.1.tgz", - "integrity": "sha512-fQO4/ZOP8LcXWvMNhKiee+2KuKyqIcfHrICA+M4lj/h/Lh1H10ICcUtk6N/chnEo5HXu0yejg64wshdaiFitJg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz", + "integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==", "requires": { - "@apollo/utils.isnodelike": "^2.0.1", + "@apollo/utils.isnodelike": "^3.0.0", "sha.js": "^2.4.11" } }, "@apollo/utils.fetcher": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-2.0.1.tgz", - "integrity": "sha512-jvvon885hEyWXd4H6zpWeN3tl88QcWnHp5gWF5OPF34uhvoR+DFqcNxs9vrRaBBSY3qda3Qe0bdud7tz2zGx1A==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz", + "integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A==" }, "@apollo/utils.isnodelike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-2.0.1.tgz", - "integrity": "sha512-w41XyepR+jBEuVpoRM715N2ZD0xMD413UiJx8w5xnAZD2ZkSJnMJBoIzauK83kJpSgNuR6ywbV29jG9NmxjK0Q==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz", + "integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g==" + }, + "@apollo/utils.keyvaluecache": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz", + "integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==", + "requires": { + "@apollo/utils.logger": "^3.0.0", + "lru-cache": "^11.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==" + } + } + }, + "@apollo/utils.logger": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz", + "integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==" }, "@apollo/utils.withrequired": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-2.0.1.tgz", - "integrity": "sha512-YBDiuAX9i1lLc6GeTy1m7DGLFn/gMnvXqlalOIMjM7DeOgIacEjjfwPqb0M1CQ2v11HhR15d1NmxJoRCfrNqcA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz", + "integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA==" + }, + "@as-integrations/express4": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@as-integrations/express4/-/express4-1.1.2.tgz", + "integrity": "sha512-PGeMcwoOKdYnZ4LtsmM7aLNoel3tbK8wKnfyahdRau1qb7wLbuaXB35zg3w34Ov4bm3WJtO3yzd8Bw5jVE+aIQ==", + "requires": {} }, "@babel/cli": { "version": "7.16.8", @@ -14100,54 +13038,64 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" } }, "@babel/compat-data": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", - "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true }, "@babel/core": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", - "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7", - "convert-source-map": "^1.7.0", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + } } }, "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" } }, "@babel/helper-annotate-as-pure": { @@ -14170,15 +13118,33 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "requires": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } } }, "@babel/helper-create-class-features-plugin": { @@ -14247,14 +13213,11 @@ "@babel/types": "^7.23.0" } }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } + "@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true }, "@babel/helper-member-expression-to-functions": { "version": "7.16.7", @@ -14266,28 +13229,24 @@ } }, "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" } }, "@babel/helper-optimise-call-expression": { @@ -14300,9 +13259,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true }, "@babel/helper-remap-async-to-generator": { @@ -14357,21 +13316,21 @@ } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true }, "@babel/helper-wrap-function": { @@ -14387,25 +13346,13 @@ } }, "@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/node": { @@ -14423,10 +13370,13 @@ } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "requires": { + "@babel/types": "^7.29.0" + } }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.16.7", @@ -14660,6 +13610,15 @@ "@babel/helper-plugin-utils": "^7.8.3" } }, + "@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.28.6" + } + }, "@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -14679,12 +13638,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", - "integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -14760,12 +13719,12 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", - "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-arrow-functions": { @@ -14931,16 +13890,15 @@ } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" } }, "@babel/plugin-transform-modules-umd": { @@ -15191,97 +14149,324 @@ } }, "@babel/runtime": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", - "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "requires": { + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } + } + }, + "@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + } + }, + "@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + } + }, + "@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" } }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@esbuild/aix-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } + "optional": true }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } + "@esbuild/win32-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "dev": true, + "optional": true }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "eslint-visitor-keys": "^3.4.3" } }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true }, - "@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", - "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", - "dev": true, - "requires": { - "lodash.get": "^4", - "make-error": "^1", - "ts-node": "^9", - "tslib": "^2" - } - }, "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -15290,29 +14475,40 @@ } } }, + "@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true + }, "@graphql-tools/merge": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz", - "integrity": "sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.6.tgz", + "integrity": "sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw==", "requires": { - "@graphql-tools/utils": "^9.2.1", + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" + } + }, + "@graphql-tools/schema": { + "version": "10.0.30", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.30.tgz", + "integrity": "sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA==", + "requires": { + "@graphql-tools/merge": "^9.1.6", + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" + } + }, + "@graphql-tools/utils": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.11.0.tgz", + "integrity": "sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==", + "requires": { + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", "tslib": "^2.4.0" - }, - "dependencies": { - "@graphql-tools/utils": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", - "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", - "requires": { - "@graphql-typed-document-node/core": "^3.1.1", - "tslib": "^2.4.0" - } - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } } }, "@graphql-typed-document-node/core": { @@ -15322,20 +14518,26 @@ "requires": {} }, "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" } }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, "@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "@ioredis/commands": { @@ -15344,6 +14546,71 @@ "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", "optional": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -15364,180 +14631,80 @@ "dev": true }, "@jest/console": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.4.6.tgz", - "integrity": "sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", + "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", "dev": true, "requires": { - "@jest/types": "^27.4.2", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", + "chalk": "^4.1.2", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", "slash": "^3.0.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "@jest/core": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.4.7.tgz", - "integrity": "sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", + "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", "dev": true, "requires": { - "@jest/console": "^27.4.6", - "@jest/reporters": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", + "@jest/console": "30.3.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^27.4.2", - "jest-config": "^27.4.7", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-resolve-dependencies": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "jest-watcher": "^27.4.6", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.3.0", + "jest-config": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-resolve-dependencies": "30.3.0", + "jest-runner": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "jest-watcher": "30.3.0", + "pretty-format": "30.3.0", + "slash": "^3.0.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "^27.4.6" - } - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "pretty-format": "30.3.0" } }, "slash": { @@ -15545,368 +14712,335 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, + "@jest/diff-sequences": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", + "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "dev": true + }, "@jest/environment": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.4.6.tgz", - "integrity": "sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", + "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", "dev": true, "requires": { - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "jest-mock": "^27.4.6" + "jest-mock": "30.3.0" + } + }, + "@jest/expect": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", + "dev": true, + "requires": { + "expect": "30.3.0", + "jest-snapshot": "30.3.0" + } + }, + "@jest/expect-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", + "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0" } }, "@jest/fake-timers": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.6.tgz", - "integrity": "sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", + "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "@sinonjs/fake-timers": "^8.0.1", + "@jest/types": "30.3.0", + "@sinonjs/fake-timers": "^15.0.0", "@types/node": "*", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" } }, + "@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true + }, "@jest/globals": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.4.6.tgz", - "integrity": "sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", + "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", "dev": true, "requires": { - "@jest/environment": "^27.4.6", - "@jest/types": "^27.4.2", - "expect": "^27.4.6" + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/types": "30.3.0", + "jest-mock": "30.3.0" + } + }, + "@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-regex-util": "30.0.1" } }, "@jest/reporters": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.6.tgz", - "integrity": "sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", + "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", + "@jest/console": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", + "istanbul-lib-source-maps": "^5.0.0", "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "balanced-match": "^1.0.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "requires": { - "color-name": "~1.1.4" + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.2" + } + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, - "@jest/source-map": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz", - "integrity": "sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==", + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "@sinclair/typebox": "^0.27.8" } }, - "@jest/test-result": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.6.tgz", - "integrity": "sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ==", + "@jest/snapshot-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", + "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", "dev": true, "requires": { - "@jest/console": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" } }, - "@jest/test-sequencer": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz", - "integrity": "sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw==", + "@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, "requires": { - "@jest/test-result": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-runtime": "^27.4.6" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" } }, - "@jest/transform": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.4.6.tgz", - "integrity": "sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw==", + "@jest/test-result": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", + "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.4.2", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-util": "^27.4.2", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, + "@jest/console": "30.3.0", + "@jest/types": "30.3.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + } + }, + "@jest/test-sequencer": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", + "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", + "dev": true, + "requires": { + "@jest/test-result": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "slash": "^3.0.0" + }, + "dependencies": { "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + } + } + }, + "@jest/transform": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", + "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true } } }, "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "requires": { - "color-name": "~1.1.4" + "@sinclair/typebox": "^0.34.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, - "@josephg/resolvable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", - "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==" - }, "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "@jridgewell/resolve-uri": { @@ -15915,33 +15049,16 @@ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.1.0", @@ -15949,79 +15066,113 @@ } }, "@lingui/babel-plugin-extract-messages": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.13.0.tgz", - "integrity": "sha512-akDiMq+CrF3m4ENA3DlEj+XfskZow6SqvrkOUVIStow5kUqcCBow635W7+YAem2TJNxH+CpVgpGV24osiQx+ZQ==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-5.4.1.tgz", + "integrity": "sha512-sjkVaLyuK3ZW62mv5gU6pOdl3ZpwDReeSaNodJuf9LssbMIQPa5WOirTnMeBaalrQ8BA2srrRzQAWgsXPQVdXA==", + "dev": true + }, + "@lingui/babel-plugin-lingui-macro": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-lingui-macro/-/babel-plugin-lingui-macro-5.4.1.tgz", + "integrity": "sha512-9IO+PDvdneY8OCI8zvI1oDXpzryTMtyRv7uq9O0U1mFCvIPVd5dWQKQDu/CpgpYAc2+JG/izn5PNl9xzPc6ckw==", "dev": true, "requires": { - "@babel/generator": "^7.11.6", - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.13.0", - "mkdirp": "^1.0.4" + "@babel/core": "^7.20.12", + "@babel/runtime": "^7.20.13", + "@babel/types": "^7.20.7", + "@lingui/conf": "5.4.1", + "@lingui/core": "5.4.1", + "@lingui/message-utils": "5.4.1" + }, + "dependencies": { + "@lingui/core": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.4.1.tgz", + "integrity": "sha512-4FeIh56PH5vziPg2BYo4XYWWOHE4XaY/XR8Jakwn0/qwtLpydWMNVpZOpGWi7nfPZtcLaJLmZKup6UNxEl1Pfw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "5.4.1" + } + }, + "@lingui/message-utils": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.4.1.tgz", + "integrity": "sha512-hXfL90fFBoKp5YgLaWo3HbJS/7q+WlWs7VwVbUxl4pa+YladqNZf08JoDeBUDtlEVx5a3bNUSACXHs2FZo12aw==", + "dev": true, + "requires": { + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" + } + } } }, "@lingui/cli": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.13.0.tgz", - "integrity": "sha512-hK/7z+hqxT9CSzUQUQEefurbjmZCJldLG9kbSp8mNgJ+XLAv1mWPve79pYCbtMK7M7vbyU4uG0ncnH+pHKFF/w==", - "dev": true, - "requires": { - "@babel/generator": "^7.11.6", - "@babel/parser": "^7.11.5", - "@babel/plugin-syntax-jsx": "^7.10.4", - "@babel/runtime": "^7.11.2", - "@babel/types": "^7.11.5", - "@lingui/babel-plugin-extract-messages": "^3.13.0", - "@lingui/conf": "^3.13.0", - "babel-plugin-macros": "^3.0.1", - "bcp-47": "^1.0.7", - "chalk": "^4.1.0", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-5.4.1.tgz", + "integrity": "sha512-UAKA9Iz4zMDJS7fzWMZ4hzQWontrTBnI5XCsPm7ttB0Ed0F4Pwph/Vu7pg4bJdiYr4d6nqEpRWd9aTxcC15/IA==", + "dev": true, + "requires": { + "@babel/core": "^7.21.0", + "@babel/generator": "^7.21.1", + "@babel/parser": "^7.22.0", + "@babel/runtime": "^7.21.0", + "@babel/types": "^7.21.2", + "@lingui/babel-plugin-extract-messages": "5.4.1", + "@lingui/babel-plugin-lingui-macro": "5.4.1", + "@lingui/conf": "5.4.1", + "@lingui/core": "5.4.1", + "@lingui/format-po": "5.4.1", + "@lingui/message-utils": "5.4.1", "chokidar": "3.5.1", - "cli-table": "0.3.6", - "commander": "^6.1.0", - "date-fns": "^2.16.1", - "fs-extra": "^9.0.1", - "fuzzaldrin": "^2.1.0", - "glob": "^7.1.4", - "inquirer": "^7.3.3", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3", - "micromatch": "4.0.2", - "mkdirp": "^1.0.4", - "node-gettext": "^3.0.0", + "cli-table": "^0.3.11", + "commander": "^10.0.0", + "convert-source-map": "^2.0.0", + "date-fns": "^3.6.0", + "esbuild": "^0.25.1", + "glob": "^11.0.0", + "micromatch": "^4.0.7", "normalize-path": "^3.0.0", "ora": "^5.1.0", - "papaparse": "^5.3.0", - "pkg-up": "^3.1.0", - "plurals-cldr": "^1.0.4", - "pofile": "^1.1.0", - "pseudolocale": "^1.1.0", - "ramda": "^0.27.1" + "picocolors": "^1.1.1", + "pofile": "^1.1.4", + "pseudolocale": "^2.0.0", + "source-map": "^0.8.0-beta.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "@lingui/core": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.4.1.tgz", + "integrity": "sha512-4FeIh56PH5vziPg2BYo4XYWWOHE4XaY/XR8Jakwn0/qwtLpydWMNVpZOpGWi7nfPZtcLaJLmZKup6UNxEl1Pfw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "5.4.1" + } + }, + "@lingui/message-utils": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.4.1.tgz", + "integrity": "sha512-hXfL90fFBoKp5YgLaWo3HbJS/7q+WlWs7VwVbUxl4pa+YladqNZf08JoDeBUDtlEVx5a3bNUSACXHs2FZo12aw==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" } }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "balanced-match": "^4.0.2" } }, "chokidar": { @@ -16040,172 +15191,248 @@ "readdirp": "~3.5.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, "requires": { - "binary-extensions": "^2.0.0" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" } }, - "make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==", - "dev": true + "minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "requires": { + "brace-expansion": "^5.0.2" + } }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "whatwg-url": "^7.0.0" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "punycode": "^2.1.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } } } }, "@lingui/conf": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.13.0.tgz", - "integrity": "sha512-1vl7NEZWMuiM2JCqnvlGmoyqlwB4isSEZrzvKWGAGMRLxMuuKR6PrH1Khgl4x2WRLZxfEysXTe6YR08Ra68irQ==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-5.4.1.tgz", + "integrity": "sha512-aDkj/bMSr/mCL8Nr1TS52v0GLCuVa4YqtRz+WvUCFZw/ovVInX0hKq1TClx/bSlhu60FzB/CbclxFMBw8aLVUg==", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", - "@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "jest-validate": "^26.5.2", - "lodash.get": "^4.4.2" + "@babel/runtime": "^7.20.13", + "cosmiconfig": "^8.0.0", + "jest-validate": "^29.4.3", + "jiti": "^1.17.1", + "picocolors": "^1.1.1" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "argparse": "^2.0.1" } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + } + } + }, + "@lingui/core": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-4.13.0.tgz", + "integrity": "sha512-kRqQWeEVoqNrDtEdyHPWGsAHRStN8ObYc5a1gdyuBhoj1zaoUS/DMK5C7B1ZeTtj6rCCmZRs6d2tN12hsZ2zJA==", + "requires": { + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "4.13.0", + "unraw": "^3.0.0" + } + }, + "@lingui/format-po": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-5.4.1.tgz", + "integrity": "sha512-IBVq3RRLNEVRzNZcdEw0qpM5NKX4e9wDmvJMorkR2OYrgTbhWx5gDYhXpEZ9yqtuEVhILMdriVNjAAUnDAJibA==", + "dev": true, + "requires": { + "@lingui/conf": "5.4.1", + "@lingui/message-utils": "5.4.1", + "date-fns": "^3.6.0", + "pofile": "^1.1.4" + }, + "dependencies": { + "@lingui/message-utils": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.4.1.tgz", + "integrity": "sha512-hXfL90fFBoKp5YgLaWo3HbJS/7q+WlWs7VwVbUxl4pa+YladqNZf08JoDeBUDtlEVx5a3bNUSACXHs2FZo12aw==", + "dev": true, + "requires": { + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" + } + } + } + }, + "@lingui/macro": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-4.13.0.tgz", + "integrity": "sha512-OjhaWOWbTCXHOOHNaGI0shMP3qrPjNZ19tpEx/iStAmJq64fkevx/HbDPI0uuqLX8v1NFWG/SzBMIQzJb5YOvA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.20.13", + "@babel/types": "^7.20.7", + "@lingui/conf": "4.13.0", + "@lingui/core": "4.13.0", + "@lingui/message-utils": "4.13.0" + }, + "dependencies": { + "@lingui/conf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-4.13.0.tgz", + "integrity": "sha512-7NSinlzgSMKBezLsSM7DMwr0IpTHKr8nuSDpTZpI79+BhW+Xq38jPRQqMXdzItW8Cl/Lsdr3Y3MnYJIl8tADsQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "@babel/runtime": "^7.20.13", + "chalk": "^4.1.0", + "cosmiconfig": "^8.0.0", + "jest-validate": "^29.4.3", + "jiti": "^1.17.1", + "lodash.get": "^4.4.2" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "argparse": "^2.0.1" } } } }, - "@lingui/core": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.13.0.tgz", - "integrity": "sha512-UDmI8UL59rLmQDDjBK8JFMX0+i3+pncl3fWG+tD2cXNJkN+MEBrhECTQ2lsM1tCk09AfiATglPPXm1e0tLxxOw==", + "@lingui/message-utils": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.13.0.tgz", + "integrity": "sha512-tI/WBVZym+APwqk0O3xTaF0k+RQIv5E4PqGHdXqwbofycHly2C+izH+hg6UeNctc6jd19GRwqu/4ga9knkdAlQ==", "requires": { - "@babel/runtime": "^7.11.2", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3" - }, - "dependencies": { - "make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" - } + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" } }, - "@lingui/loader": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-3.13.0.tgz", - "integrity": "sha512-MuLwM6vVMYH/ilCGunNGIPTvZU9gtjaohU54/bDCRbDJ5teN46UKqFTYqP6TwVWa+AF6hRMcgUprBcQJi7Iw8Q==", + "@lingui/react": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@lingui/react/-/react-4.13.0.tgz", + "integrity": "sha512-5remR9rVwosiiX/RnEWETHA8cpqQiP7U87OXXMPz67LuyG3XP8RP+ic75rVn284DHLHgpjDbauz7vYIz855ZoQ==", "dev": true, + "peer": true, "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/cli": "^3.13.0", - "@lingui/conf": "^3.13.0", - "loader-utils": "^2.0.0", - "ramda": "^0.27.1" + "@babel/runtime": "^7.20.13", + "@lingui/core": "4.13.0" } }, - "@lingui/macro": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.13.0.tgz", - "integrity": "sha512-TmwAiFnxtutDEKp7KFtUmq5vIfv56zn0FV0ZgrISUcW1liVlRyqW6YnQ7cv4AzsPnkBhO2+O2YVoHY1r5owMvA==", + "@messageformat/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==", + "requires": { + "moo": "^0.5.1" + } + }, + "@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", "dev": true, + "optional": true, "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.13.0", - "ramda": "^0.27.1" + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" } }, "@nicolo-ribaudo/chokidar-2": { @@ -16215,6 +15442,12 @@ "dev": true, "optional": true }, + "@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -16241,6 +15474,28 @@ "fastq": "^1.6.0" } }, + "@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "requires": { + "@noble/hashes": "^1.1.5" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -16291,47 +15546,52 @@ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==" + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true }, "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", + "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^3.0.1" } }, - "@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true + "@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } }, "@types/babel__core": { - "version": "7.1.17", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.17.tgz", - "integrity": "sha512-6zzkezS9QEIL8yCBvXWxPTJPNuMeECJVxSOhxNY/jfq9LxOTHivaYTqr37n9LknWWRTIkzqH2UilS5QFvfa90A==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -16349,102 +15609,26 @@ "@types/babel__template": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/eslint": { - "version": "8.21.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.2.tgz", - "integrity": "sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw==", - "dev": true, - "peer": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true, - "peer": true - }, - "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", "dev": true, "requires": { - "@types/node": "*" + "@babel/types": "^7.3.0" } }, "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "@types/istanbul-lib-report": { @@ -16457,20 +15641,14 @@ } }, "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "requires": { "@types/istanbul-lib-report": "*" } }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -16482,23 +15660,12 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, "@types/node": { - "version": "16.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", - "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" - }, - "@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", "requires": { - "@types/node": "*", - "form-data": "^3.0.0" + "undici-types": "~6.21.0" } }, "@types/parse-json": { @@ -16507,50 +15674,16 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "@types/prettier": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz", - "integrity": "sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -16562,263 +15695,268 @@ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", "dev": true }, - "@typescript-eslint/experimental-utils": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.1.tgz", - "integrity": "sha512-NtoPsqmcSsWty0mcL5nTZXMf7Ei0Xr2MT8jWjXMVgRK0/1qeQ2jZzLFUh4QtyJ4+/lPUyMw5cSfeeME+Zrtp9Q==", + "@typescript-eslint/project-service": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "requires": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.31.1", - "@typescript-eslint/types": "4.31.1", - "@typescript-eslint/typescript-estree": "4.31.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "dependencies": { - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - } + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "debug": "^4.4.3" } }, "@typescript-eslint/scope-manager": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.31.1.tgz", - "integrity": "sha512-N1Uhn6SqNtU2XpFSkD4oA+F0PfKdWHyr4bTX0xTj8NRx1314gBDRL1LUuZd5+L3oP+wo6hCbZpaa1in6SwMcVQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", "dev": true, "requires": { - "@typescript-eslint/types": "4.31.1", - "@typescript-eslint/visitor-keys": "4.31.1" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" } }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "dev": true, + "requires": {} + }, "@typescript-eslint/types": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.1.tgz", - "integrity": "sha512-kixltt51ZJGKENNW88IY5MYqTBA8FR0Md8QdGbJD2pKZ+D5IvxjTYDNtJPDxFBiXmka2aJsITdB1BtO1fsgmsQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.1.tgz", - "integrity": "sha512-EGHkbsUvjFrvRnusk6yFGqrqMBTue5E5ROnS5puj3laGQPasVUgwhrxfcgkdHNFECHAewpvELE1Gjv0XO3mdWg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.31.1", - "@typescript-eslint/visitor-keys": "4.31.1", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, + "brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "balanced-match": "^4.0.2" + } + }, + "minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "requires": { + "brace-expansion": "^5.0.2" } + }, + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true } } }, - "@typescript-eslint/visitor-keys": { - "version": "4.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.1.tgz", - "integrity": "sha512-PCncP8hEqKw6SOJY+3St4LVtoZpPPn+Zlpm7KW5xnviMhdqcsBty4Lsg4J/VECpJjw1CkROaZhH4B8M1OfnXTQ==", + "@typescript-eslint/utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.31.1", - "eslint-visitor-keys": "^2.0.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" } }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "@typescript-eslint/visitor-keys": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", "dev": true, - "peer": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@typescript-eslint/types": "8.57.2", + "eslint-visitor-keys": "^5.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true + } } }, - "@webassemblyjs/floating-point-hex-parser": { + "@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "@unrs/resolver-binding-android-arm-eabi": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", "dev": true, - "peer": true + "optional": true }, - "@webassemblyjs/helper-api-error": { + "@unrs/resolver-binding-android-arm64": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", "dev": true, - "peer": true + "optional": true }, - "@webassemblyjs/helper-buffer": { + "@unrs/resolver-binding-darwin-arm64": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", "dev": true, - "peer": true + "optional": true }, - "@webassemblyjs/helper-numbers": { + "@unrs/resolver-binding-darwin-x64": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } + "optional": true }, - "@webassemblyjs/helper-wasm-bytecode": { + "@unrs/resolver-binding-freebsd-x64": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", "dev": true, - "peer": true + "optional": true }, - "@webassemblyjs/helper-wasm-section": { + "@unrs/resolver-binding-linux-arm-gnueabihf": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } + "optional": true }, - "@webassemblyjs/ieee754": { + "@unrs/resolver-binding-linux-arm-musleabihf": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", "dev": true, - "peer": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } + "optional": true }, - "@webassemblyjs/leb128": { + "@unrs/resolver-binding-linux-arm64-gnu": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", "dev": true, - "peer": true, - "requires": { - "@xtuc/long": "4.2.2" - } + "optional": true }, - "@webassemblyjs/utf8": { + "@unrs/resolver-binding-linux-arm64-musl": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", "dev": true, - "peer": true + "optional": true }, - "@webassemblyjs/wasm-edit": { + "@unrs/resolver-binding-linux-ppc64-gnu": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } + "optional": true }, - "@webassemblyjs/wasm-gen": { + "@unrs/resolver-binding-linux-riscv64-gnu": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } + "optional": true }, - "@webassemblyjs/wasm-opt": { + "@unrs/resolver-binding-linux-riscv64-musl": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } + "optional": true }, - "@webassemblyjs/wasm-parser": { + "@unrs/resolver-binding-linux-s390x-gnu": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } + "optional": true }, - "@webassemblyjs/wast-printer": { + "@unrs/resolver-binding-linux-x64-gnu": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", "dev": true, - "peer": true, + "optional": true + }, + "@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "dev": true, + "optional": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" + "@napi-rs/wasm-runtime": "^0.2.11" } }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", "dev": true, - "peer": true + "optional": true + }, + "@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "dev": true, + "optional": true }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", "dev": true, - "peer": true + "optional": true }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true + "@whatwg-node/promise-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", + "requires": { + "tslib": "^2.6.3" + } }, "accept-language": { "version": "3.0.18", @@ -16836,24 +15974,29 @@ "requires": { "mime-types": "~2.1.34", "negotiator": "0.6.3" + }, + "dependencies": { + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + } } }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, + "accesscontrol": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/accesscontrol/-/accesscontrol-2.2.1.tgz", + "integrity": "sha512-52EvFk/J9EF+w4mYQoKnOTkEMj01R1U5n2fc1dai6x1xkgOks3DGkx01qQL2cKFxGmE4Tn1krAU3jJA9L1NMkg==", "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" + "notation": "^1.3.6" } }, + "acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true + }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -16861,25 +16004,10 @@ "dev": true, "requires": {} }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -16888,20 +16016,6 @@ "uri-js": "^4.2.2" } }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peer": true, - "requires": {} - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -16918,18 +16032,18 @@ "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -16984,23 +16098,13 @@ } }, "arangojs": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-8.0.0.tgz", - "integrity": "sha512-VjeWxbS18c65Wu6Pr/d5/kW0Chn6ftIfUAzCmG3wUUe8OrRRalSgL6e1+5iY9apocbECo6wTK3kr2goXfLHB+A==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-10.2.2.tgz", + "integrity": "sha512-3Xllq5inTGjros0mBP9NFxrIW8Di0ldtFurLdrXy5z4NDVJPyJtnwUiiGrMPY21NuVu53wUDE23YN50jnX4epw==", "requires": { - "@types/node": ">=14", - "multi-part": "^4.0.0", - "path-browserify": "^1.0.1", - "x3-linkedlist": "1.2.0", - "xhr": "^2.4.1" + "@types/node": "^20.11.26" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -17013,7 +16117,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-includes": { "version": "3.1.4", @@ -17028,12 +16132,6 @@ "is-string": "^1.0.7" } }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -17069,12 +16167,6 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-2.0.2.tgz", "integrity": "sha512-9sBQUQZMKFKcO/C3Bo6Rx4CQany0R0UeVcefNGRRdW2vbmaMOhV1sbmlXcQLcD56juLXbSGTBm0GGuvmrAF8pA==" }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", @@ -17088,18 +16180,22 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "requires": { + "possible-typed-array-names": "^1.0.0" + } }, "axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", "requires": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.16.0", + "form-data": "4.0.4", + "proxy-from-env": "^2.1.0" } }, "babel-core": { @@ -17110,75 +16206,25 @@ "requires": {} }, "babel-jest": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.6.tgz", - "integrity": "sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", + "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", "dev": true, "requires": { - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.4.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", + "@jest/transform": "30.3.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", "slash": "^3.0.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, @@ -17192,28 +16238,25 @@ } }, "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", "test-exclude": "^6.0.0" } }, "babel-plugin-jest-hoist": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz", - "integrity": "sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", + "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", "dev": true, "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" + "@types/babel__core": "^7.20.5" } }, "babel-plugin-macros": { @@ -17283,33 +16326,36 @@ } }, "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, "requires": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" } }, "babel-preset-jest": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz", - "integrity": "sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", + "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^27.4.0", - "babel-preset-current-node-syntax": "^1.0.0" + "babel-plugin-jest-hoist": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0" } }, "babel-runtime": { @@ -17336,11 +16382,6 @@ } } }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -17353,17 +16394,6 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, - "bcp-47": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", - "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "bcp47": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", @@ -17374,10 +16404,10 @@ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true }, "bl": { @@ -17392,86 +16422,43 @@ } }, "body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "requires": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "ms": "2.0.0" } }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -17479,31 +16466,24 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, "browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" } }, "bser": { @@ -17528,7 +16508,7 @@ "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "buffer-from": { "version": "1.1.2", @@ -17536,18 +16516,61 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, + "builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "requires": { + "semver": "^7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + } + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "requires": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + } + }, + "call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" } }, "callsites": { @@ -17563,20 +16586,36 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001286", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", - "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", + "version": "1.0.30001675", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001675.tgz", + "integrity": "sha512-/wV1bQwPrkLiQMjaJF5yUMVM/VdRPOCU8QZ+PmG6uW6DvYSrNY1bpwHI/3mOcUosLaJCzYDi5o91IQB51ft6cg==", "dev": true }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "char-regex": { @@ -17585,18 +16624,11 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "chokidar": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, - "optional": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -17608,52 +16640,27 @@ "readdirp": "~3.6.0" }, "dependencies": { - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "optional": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "optional": true, "requires": { "picomatch": "^2.2.1" } } } }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "peer": true - }, "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true }, "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "dev": true }, "cli-cursor": { @@ -17672,28 +16679,22 @@ "dev": true }, "cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, "requires": { "colors": "1.0.3" } }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, @@ -17722,34 +16723,34 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "colors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", "dev": true }, "combined-stream": { @@ -17773,11 +16774,58 @@ "dev": true }, "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "requires": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -17814,16 +16862,16 @@ } }, "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" }, "cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "requires": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" } }, @@ -17845,21 +16893,12 @@ "dev": true }, "core-js-compat": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.3.tgz", - "integrity": "sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", "dev": true, "requires": { - "browserslist": "^4.19.1", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "browserslist": "^4.22.2" } }, "cors": { @@ -17884,12 +16923,6 @@ "yaml": "^1.10.0" } }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "cross-fetch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", @@ -17899,10 +16932,18 @@ "node-fetch": "2.6.7" } }, + "cross-inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", + "requires": { + "tslib": "^2.4.0" + } + }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -17910,77 +16951,31 @@ "which": "^2.0.1" } }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, "dataloader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==" }, "date-fns": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.23.0.tgz", - "integrity": "sha512-5ycpauovVyAk0kXNZz6ZoB9AYMZB4DObse7P3BPWmyEjXNORTI8EJ6X0uaSAq4sCHzM1uajzrkr6HnsLQpxGXA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "dev": true }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } + "ms": "^2.1.3" } }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", - "dev": true - }, "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "requires": {} }, "deep-is": { "version": "0.1.4", @@ -17989,9 +16984,9 @@ "dev": true }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, "defaults": { @@ -18003,6 +16998,16 @@ "clone": "^1.0.2" } }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -18023,14 +17028,14 @@ "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "detect-newline": { "version": "3.1.0", @@ -18048,27 +17053,6 @@ "wrappy": "1" } }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", - "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -18083,23 +17067,6 @@ "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, "dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", @@ -18113,6 +17080,22 @@ "dotenv": "^8.2.0" } }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -18124,18 +17107,18 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.17", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.17.tgz", - "integrity": "sha512-zhk1MravPtq/KBhmGB7TLBILmXTgRG9TFSI3qS3DbgyfHzIl72iiTE37r/BHIbPCJJlWIo5rySyxiH4vWhu2ZA==", + "version": "1.5.49", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz", + "integrity": "sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==", "dev": true }, "emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true }, "emoji-regex": { @@ -18144,58 +17127,10 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "peer": true, - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "peer": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dev": true, - "peer": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" }, "error-ex": { "version": "1.3.2", @@ -18234,13 +17169,35 @@ "unbox-primitive": "^1.0.1" } }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true, - "peer": true - }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -18257,218 +17214,176 @@ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" }, + "esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" + } + }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - } - } + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true }, "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "is-glob": "^4.0.3" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "type-fest": "^0.20.2" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { - "color-name": "~1.1.4" + "argparse": "^2.0.1" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "type-fest": "^0.20.2" + "p-locate": "^5.0.0" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "yocto-queue": "^0.1.0" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "p-limit": "^3.0.2" } }, "type-fest": { @@ -18479,6 +17394,23 @@ } } }, + "eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "requires": { + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + } + } + }, "eslint-config-prettier": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", @@ -18487,9 +17419,9 @@ "requires": {} }, "eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", "dev": true, "requires": {} }, @@ -18584,14 +17516,15 @@ } } }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", "dev": true, "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" } }, "eslint-plugin-import": { @@ -18642,93 +17575,88 @@ } }, "eslint-plugin-jest": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.7.0.tgz", - "integrity": "sha512-wUxdF2bAZiYSKBclsUMrYHH6WxiBreNjyDxbRv345TIvPeoCEgPNEn3Sa+ZrSqsf1Dl9SqqSREXMHExlMMu1DA==", + "version": "29.15.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.1.tgz", + "integrity": "sha512-6BjyErCQauz3zfJvzLw/kAez2lf4LEpbHLvWBfEcG4EI0ZiRSwjoH2uZulMouU8kRkBH+S0rhqn11IhTvxKgKw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "^4.0.1" + "@typescript-eslint/utils": "^8.0.0" } }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" }, "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } } }, "eslint-plugin-promise": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz", - "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "requires": {} }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { @@ -18738,20 +17666,12 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "esrecurse": { @@ -18761,20 +17681,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -18786,19 +17698,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "peer": true + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "execa": { "version": "5.1.1", @@ -18817,83 +17717,64 @@ "strip-final-newline": "^2.0.0" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", "dev": true }, "expect": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz", - "integrity": "sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "jest-get-type": "^27.4.0", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6" + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" } }, "express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", + "depd": "2.0.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.9.7", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { - "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - } - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -18902,21 +17783,24 @@ "ms": "2.0.0" } }, + "finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "requires": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "safe-buffer": { "version": "5.2.1", @@ -18934,48 +17818,12 @@ "bcp47": "^1.1.2" } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - } - } - }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -18985,7 +17833,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fast-safe-stringify": { @@ -19004,23 +17852,14 @@ } }, "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "requires": { "bser": "2.1.1" } }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -19030,52 +17869,26 @@ "flat-cache": "^3.0.4" } }, - "file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "requires": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - } - }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" } }, "find-cache-dir": { @@ -19110,47 +17923,63 @@ } }, "flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==" + }, + "for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "requires": { + "is-callable": "^1.2.7" + } + }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "formidable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", - "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, "requires": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "dependencies": { - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - } + "once": "^1.4.0" } }, "forwarded": { @@ -19161,19 +17990,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-readdir-recursive": { "version": "1.1.0", @@ -19188,28 +18005,16 @@ "dev": true }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "fuzzaldrin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", - "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "gensync": { "version": "1.0.0-beta.2", @@ -19224,13 +18029,20 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" } }, "get-package-type": { @@ -19239,6 +18051,15 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -19255,6 +18076,15 @@ "get-intrinsic": "^1.1.1" } }, + "get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -19278,13 +18108,6 @@ "is-glob": "^4.0.1" } }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true - }, "global": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", @@ -19300,44 +18123,27 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==" + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", + "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==" }, "graphql-depth-limit": { "version": "1.1.0", @@ -19381,24 +18187,17 @@ } }, "graphql-relay": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.0.tgz", - "integrity": "sha512-44yBuw2/DLNEiMypbNZBt1yMDbBmyVPVesPywnteGGALiBmdyy1JP8jSg8ClLePg8ZZxk0O4BLhd1a6U/1jDOQ==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.2.tgz", + "integrity": "sha512-abybva1hmlNt7Y9pMpAzHuFnM2Mme/a2Usd8S4X27fNteLGRAECMYfhmsrpZFvGn3BhmBZugMXYW/Mesv3P1Kw==", "requires": {} }, "graphql-scalars": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.22.4.tgz", - "integrity": "sha512-ILnv7jq5VKHLUyoaTFX7lgYrjCd6vTee9i8/B+D4zJKJT5TguOl0KkpPEbXHjmeor8AZYrVsrYUHdqRBMX1pjA==", + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.25.0.tgz", + "integrity": "sha512-b0xyXZeRFkne4Eq7NAnL400gStGqG/Sx9VqX0A05nHyEbv57UJnWKsjNnrpVqv5e/8N1MUxkt0wwcRXbiyKcFg==", "requires": { "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } } }, "graphql-subscriptions": { @@ -19421,6 +18220,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -19437,25 +18237,34 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { + "has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" }, "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "requires": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" } }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } }, "homedir-polyfill": { "version": "1.0.3", @@ -19466,15 +18275,6 @@ "parse-passwd": "^1.0.0" } }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -19482,36 +18282,15 @@ "dev": true }, "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "requires": { - "agent-base": "6", - "debug": "4" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" } }, "human-signals": { @@ -19531,12 +18310,19 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, "import-fresh": { @@ -19558,9 +18344,9 @@ } }, "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -19599,78 +18385,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -19705,22 +18419,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -19736,6 +18434,15 @@ "has-bigints": "^1.0.1" } }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -19746,19 +18453,27 @@ "has-tostringtag": "^1.0.0" } }, + "is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "requires": { + "builtin-modules": "^3.3.0" + } + }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.2" } }, "is-date-object": { @@ -19770,12 +18485,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -19835,6 +18544,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -19844,12 +18559,6 @@ "isobject": "^3.0.1" } }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -19890,11 +18599,13 @@ "has-symbols": "^1.0.2" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "requires": { + "which-typed-array": "^1.1.16" + } }, "is-unicode-supported": { "version": "0.1.0", @@ -19911,6 +18622,11 @@ "call-bind": "^1.0.2" } }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -19933,32 +18649,40 @@ } }, "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true }, "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + } } }, "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "dependencies": { @@ -19969,14 +18693,20 @@ "dev": true }, "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "requires": { - "semver": "^6.0.0" + "semver": "^7.5.3" } }, + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -19989,28 +18719,20 @@ } }, "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "istanbul-lib-coverage": "^3.0.0" } }, "istanbul-reports": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", - "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -20022,240 +18744,172 @@ "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" }, + "jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2" + } + }, "jest": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz", - "integrity": "sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", + "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "requires": { - "@jest/core": "^27.4.7", - "import-local": "^3.0.2", - "jest-cli": "^27.4.7" + "@jest/core": "30.3.0", + "@jest/types": "30.3.0", + "import-local": "^3.2.0", + "jest-cli": "30.3.0" } }, "jest-changed-files": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz", - "integrity": "sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", + "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "execa": "^5.0.0", - "throat": "^6.0.1" + "execa": "^5.1.1", + "jest-util": "30.3.0", + "p-limit": "^3.1.0" + }, + "dependencies": { + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + } } }, "jest-circus": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.6.tgz", - "integrity": "sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", + "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", "dev": true, "requires": { - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", + "chalk": "^4.1.2", "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "p-limit": "^3.1.0", + "pretty-format": "30.3.0", + "pure-rand": "^7.0.0", "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" + "stack-utils": "^2.0.6" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "yocto-queue": "^0.1.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "jest-cli": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.7.tgz", - "integrity": "sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", + "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", "dev": true, "requires": { - "@jest/core": "^27.4.7", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "jest-config": "^27.4.7", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "prompts": "^2.0.1", - "yargs": "^16.2.0" + "@jest/core": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "yargs": "^17.7.2" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "^27.4.6" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" + "pretty-format": "30.3.0" } } } }, "jest-config": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.4.7.tgz", - "integrity": "sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw==", - "dev": true, - "requires": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.4.6", - "@jest/types": "^27.4.2", - "babel-jest": "^27.4.6", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-circus": "^27.4.6", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-jasmine2": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", - "slash": "^3.0.0" + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", + "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.3.0", + "@jest/types": "30.3.0", + "babel-jest": "30.3.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.3.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-runner": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "parse-json": "^5.2.0", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "balanced-match": "^1.0.0" } }, "camelcase": { @@ -20264,59 +18918,67 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "requires": { - "color-name": "~1.1.4" + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.3.0" + } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", + "minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" + "brace-expansion": "^2.0.2" } }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "slash": { @@ -20324,183 +18986,80 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "jest-diff": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz", - "integrity": "sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", "dev": true, "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.4.0", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" } }, "jest-docblock": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz", - "integrity": "sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "requires": { - "detect-newline": "^3.0.0" + "detect-newline": "^3.1.0" } }, "jest-each": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.4.6.tgz", - "integrity": "sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", + "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "jest-util": "30.3.0", + "pretty-format": "30.3.0" + } + }, + "jest-environment-node": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", + "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", + "dev": true, + "requires": { + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-mock": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.3.0" } } } }, - "jest-environment-jsdom": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz", - "integrity": "sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2", - "jsdom": "^16.6.0" - } - }, - "jest-environment-node": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.6.tgz", - "integrity": "sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" - } - }, "jest-fetch-mock": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", @@ -20511,377 +19070,144 @@ "promise-polyfill": "^8.1.3" } }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, "jest-haste-map": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.6.tgz", - "integrity": "sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", + "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "@types/graceful-fs": "^4.1.2", + "@jest/types": "30.3.0", "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^27.4.0", - "jest-serializer": "^27.4.0", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "dependencies": { - "micromatch": { + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "fsevents": "^2.3.3", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "dependencies": { + "picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - } - } - }, - "jest-jasmine2": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz", - "integrity": "sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", - "throat": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "jest-leak-detector": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz", - "integrity": "sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", + "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", "dev": true, "requires": { - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" + "@jest/get-type": "30.1.0", + "pretty-format": "30.3.0" } }, "jest-matcher-utils": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz", - "integrity": "sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", + "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", "dev": true, "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.3.0", + "pretty-format": "30.3.0" } }, "jest-message-util": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz", - "integrity": "sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.4.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", + "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.3.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3", + "pretty-format": "30.3.0", "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "stack-utils": "^2.0.6" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "micromatch": { + "picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "jest-mock": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.6.tgz", - "integrity": "sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", + "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "@types/node": "*" + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-util": "30.3.0" } }, "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "requires": {} }, "jest-regex-util": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz", - "integrity": "sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==", + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true }, "jest-resolve": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.6.tgz", - "integrity": "sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", + "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "^27.4.6" + "pretty-format": "30.3.0" } }, "slash": { @@ -20889,548 +19215,319 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "jest-resolve-dependencies": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz", - "integrity": "sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", + "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", "dev": true, "requires": { - "@jest/types": "^27.4.2", - "jest-regex-util": "^27.4.0", - "jest-snapshot": "^27.4.6" + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.3.0" } }, "jest-runner": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.6.tgz", - "integrity": "sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", + "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", "dev": true, "requires": { - "@jest/console": "^27.4.6", - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", + "@jest/console": "30.3.0", + "@jest/environment": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-docblock": "^27.4.0", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-haste-map": "^27.4.6", - "jest-leak-detector": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-leak-detector": "30.3.0", + "jest-message-util": "30.3.0", + "jest-resolve": "30.3.0", + "jest-runtime": "30.3.0", + "jest-util": "30.3.0", + "jest-watcher": "30.3.0", + "jest-worker": "30.3.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "yocto-queue": "^0.1.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } } } }, "jest-runtime": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.6.tgz", - "integrity": "sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/globals": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", + "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", + "dev": true, + "requires": { + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/globals": "30.3.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "balanced-match": "^1.0.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "requires": { - "color-name": "~1.1.4" + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.2" + } + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, - "jest-serializer": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz", - "integrity": "sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } - }, "jest-snapshot": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.6.tgz", - "integrity": "sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-haste-map": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", - "natural-compare": "^1.4.0", - "pretty-format": "^27.4.6", - "semver": "^7.3.2" + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", + "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "pretty-format": "30.3.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true } } }, "jest-util": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz", - "integrity": "sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", + "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "requires": { - "@jest/types": "^27.4.2", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.4", - "picomatch": "^2.2.3" + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^26.6.2" + "pretty-format": "^29.7.0" }, "dependencies": { "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "requires": { + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^15.0.0", + "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, - "@types/yargs": { - "version": "15.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", - "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true }, "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } } } } }, "jest-watcher": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.6.tgz", - "integrity": "sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", + "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", "dev": true, "requires": { - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.4.2", - "string-length": "^4.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.3.0", + "string-length": "^4.0.2" } }, "jest-worker": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", - "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", + "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", "dev": true, "requires": { "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.3.0", "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "supports-color": "^8.1.1" }, "dependencies": { "has-flag": { @@ -21450,68 +19547,36 @@ } } }, + "jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true + }, + "js-sha256": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz", + "integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - } - } - }, "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true }, "json-parse-even-better-errors": { @@ -21543,25 +19608,21 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, "jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "requires": { "jws": "^3.2.2", - "lodash": "^4.17.21", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "dependencies": { "semver": { @@ -21575,21 +19636,21 @@ } }, "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "requires": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "requires": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -21599,12 +19660,6 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -21627,24 +19682,6 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true, - "peer": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -21654,17 +19691,6 @@ "p-locate": "^4.1.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -21687,28 +19713,57 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -21717,57 +19772,6 @@ "requires": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "loglevel": { @@ -21814,12 +19818,6 @@ } } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "make-plural": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.1.0.tgz", @@ -21834,15 +19832,20 @@ "tmpl": "1.0.5" } }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" }, "merge-stream": { "version": "2.0.0", @@ -21850,30 +19853,19 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.3", + "picomatch": "^2.3.1" } }, "mime": { @@ -21886,15 +19878,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, - "mime-kind": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-4.0.0.tgz", - "integrity": "sha512-qQvglvSpS5mABi30beNFd+uHKtKkxD3dxAmhi2e589XKx+WfVqhg5i5P5LBcVgwwv3BiDpNMBWrHqU+JexW4aA==", - "requires": { - "file-type": "^16.5.4", - "mime-types": "^2.1.24" - } - }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", @@ -21910,17 +19893,17 @@ "dev": true }, "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.1.tgz", + "integrity": "sha512-8lqe85PkqQJzIcs2iD7xW/WSxcncC3/DPVbTOafKNJDIMXwGfwXS350mH4SJslomntN2iYtFBuC0yNO3CEap6g==", "requires": { "dom-walk": "^0.1.0" } }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -21932,10 +19915,10 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true }, "moment": { @@ -21943,37 +19926,33 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, + "moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "multi-part": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-4.0.0.tgz", - "integrity": "sha512-YT/CS0PAe62kT8EoQXcQj8yIcSu18HhYv0s6ShdAFsoFly3oV5QaxODnkj0u7zH0/RFyH47cdcMVpcGXliEFVA==", - "requires": { - "mime-kind": "^4.0.0", - "multi-part-lite": "^1.0.0" - } - }, "multi-part-lite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/multi-part-lite/-/multi-part-lite-1.0.0.tgz", "integrity": "sha512-KxIRbBZZ45hoKX1ROD/19wJr0ql1bef1rE8Y1PCwD3PuNXV42pp7Wo8lEHYuAajoT4vfAFcd3rPjlkyEEyt1nw==" }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true }, "nats": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-2.7.0.tgz", - "integrity": "sha512-KZ6kDoDsFZq8aPFtH72DzvRKzEk4fzRImb0A8vkHi/KQzlt4VXKLaRLdkAnIUBp08+dEfb47LPDRB4JTJqhuZQ==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.18.0.tgz", + "integrity": "sha512-zZF004ejzf67Za0Tva+xphxoxBMNc5IMLqbZ7Ho0j9TMuisjpo+qCd1EktXRCLNxmrZ8O6Tbm1dBsZYNF6yR1A==", "requires": { - "nkeys.js": "^1.0.0-9" + "nkeys.js": "1.0.5" } }, "natural-compare": { @@ -21983,38 +19962,18 @@ "dev": true }, "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==" }, "nkeys.js": { - "version": "1.0.0-9", - "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.0.0-9.tgz", - "integrity": "sha512-m9O0NQT+3rUe1om6MWpxV77EuHql/LdorDH+FYQkoeARcM2V0sQ89kM36fArWaHWq/25EmNmQUW0MhLTcbqW1A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.0.5.tgz", + "integrity": "sha512-u25YnRPHiGVsNzwyHnn+PT90sgAhnS8jUJ1nxmkHMFYCJ6+Ic0lv291w7uhRBpJVJ3PH2GWbYqA151lGCRrB5g==", "requires": { - "@types/node": "^14.0.26", - "tweetnacl": "^1.0.3" - }, - "dependencies": { - "@types/node": { - "version": "14.18.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.10.tgz", - "integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ==" - } + "tweetnacl": "1.0.3" } }, - "node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" - }, "node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -22062,40 +20021,62 @@ } } }, - "node-gettext": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", - "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", - "dev": true, - "requires": { - "lodash.get": "^4.4.2" - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true + } + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "notation": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/notation/-/notation-1.3.6.tgz", + "integrity": "sha512-DIuJmrP/Gg1DcXKaApsqcjsJD6jEccqKSfmU3BUx/f1GHsMiTJh70cERwYc64tOmTRTARCeMwkqNNzjh3AHhiw==" + }, "notifications-node-client": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/notifications-node-client/-/notifications-node-client-7.0.3.tgz", - "integrity": "sha512-uLuABa7ZK2XIP6aahHbtiCP4noyNIGclJ2idPh7cojnk2retajkpo0AZctSsDP18SUB0kUmHbBxOOJm2Gkb60g==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/notifications-node-client/-/notifications-node-client-8.2.1.tgz", + "integrity": "sha512-wyZh/NbjN8S2uQX18utYtCyC726BBaGeTc4HeUpdhZv5sYKuaQY94N31v9syh8SzVgehyMzW37y08EePmi+k3Q==", "requires": { - "axios": "^0.25.0", - "jsonwebtoken": "^9.0.0" + "axios": "^1.7.2", + "jsonwebtoken": "^9.0.2" } }, "npm-run-path": { @@ -22107,21 +20088,15 @@ "path-key": "^3.0.0" } }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" }, "object-keys": { "version": "1.1.1", @@ -22164,13 +20139,18 @@ } }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -22190,9 +20170,9 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", @@ -22200,7 +20180,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" } }, "ora": { @@ -22218,65 +20198,8 @@ "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -22306,10 +20229,10 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "papaparse": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz", - "integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==", + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, "parent-module": { @@ -22344,22 +20267,11 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, - "path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -22382,97 +20294,65 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pirates": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", - "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "requires": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "dependencies": { + "lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "dev": true } } }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { "find-up": "^3.0.0" @@ -22514,18 +20394,17 @@ } } }, - "plurals-cldr": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/plurals-cldr/-/plurals-cldr-1.0.4.tgz", - "integrity": "sha512-4nLXqtel7fsCgzi8dvRZvUjfL8SXpP982sKg7b2TgpnR8rDnes06iuQ83trQ/+XdtyMIQkBBbKzX6x97eLfsJQ==", - "dev": true - }, "pofile": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.1.tgz", - "integrity": "sha512-RVAzFGo1Mx9+YukVKSgTLut6r4ZVBW8IVrqGHAPfEsVJN93WSp5HRD6+qNa7av1q/joPKDNJd55m5AJl9GBQGA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.4.tgz", + "integrity": "sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g==", "dev": true }, + "possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==" + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -22539,16 +20418,31 @@ "dev": true }, "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "dependencies": { + "@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.34.0" + } + }, + "@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "dev": true + }, "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -22562,28 +20456,12 @@ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "promise-polyfill": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.0.tgz", "integrity": "sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g==", "dev": true }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -22593,19 +20471,32 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==" + }, "pseudolocale": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-1.2.0.tgz", - "integrity": "sha512-k0OQFvIlvpRdzR0dPVrrbWX7eE9EaZ6gpZtTlFSDi1Gf9tMy9wiANCNu7JZ0drcKgUri/39a2mBbH0goiQmrmQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-2.1.0.tgz", + "integrity": "sha512-af5fsrRvVwD+MBasBJvuDChT0KDqT0nEwD9NTgbtHJ16FKomWac9ua0z6YVNB4G9x9IOaiGWym62aby6n4tFMA==", "dev": true, "requires": { - "commander": "*" + "commander": "^10.0.0" + }, + "dependencies": { + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true + } } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, "punycode": { @@ -22614,101 +20505,76 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", "dev": true }, + "qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "requires": { + "side-channel": "^1.1.0" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - } + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + } + }, + "react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "peer": true, + "requires": { + "loose-envify": "^1.1.0" } }, "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, - "readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, "requires": { - "readable-stream": "^3.6.0" + "picomatch": "^2.2.1" } }, "redis-commands": { @@ -22747,7 +20613,8 @@ "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "regenerator-transform": { "version": "0.14.5", @@ -22758,12 +20625,6 @@ "@babel/runtime": "^7.8.4" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "regexpu-core": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", @@ -22804,29 +20665,18 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-cwd": { @@ -22844,10 +20694,10 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true }, "restore-cursor": { @@ -22880,12 +20730,6 @@ "glob": "^7.1.3" } }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -22895,23 +20739,6 @@ "queue-microtask": "^1.2.2" } }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -22922,27 +20749,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "peer": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -22950,23 +20756,23 @@ "dev": true }, "send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "~2.3.0", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "~2.0.2" }, "dependencies": { "debug": { @@ -22980,31 +20786,34 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } } } }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "peer": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "requires": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "~0.19.1" + } + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" } }, "setprototypeof": { @@ -23013,12 +20822,20 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "shallow-clone": { @@ -23046,13 +20863,47 @@ "dev": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" } }, "signal-exit": { @@ -23061,55 +20912,29 @@ "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", "dev": true }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { + "simple-update-notifier": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "semver": "^7.5.3" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true } } }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -23137,7 +20962,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "stable": { @@ -23146,20 +20971,12 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" }, "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "requires": { "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } } }, "standard-as-callback": { @@ -23168,14 +20985,15 @@ "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -23183,7 +21001,8 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true } } }, @@ -23198,14 +21017,25 @@ } }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "string.prototype.trimend": { @@ -23229,12 +21059,21 @@ } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -23255,76 +21094,34 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, - "strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "requires": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" - } - }, - "subscriptions-transport-ws": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", - "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "requires": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" - } - }, "superagent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.0.2.tgz", - "integrity": "sha512-2Kx35bZxLLJMBKtuXezxvD0aZQ7l923VwoCn7EtUx+aFxdG7co7PeRIddfrNtvvMuGaLZXA0mKzX+yWRhjrJ7A==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.1.tgz", + "integrity": "sha512-CcRSdb/P2oUVaEpQ87w9Obsl+E9FruRd6b2b7LdiBtJoyMr2DQt7a89anAfiX/EL59j9b2CbRFvf2S91DhuCww==", "dev": true, "requires": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", - "debug": "^4.3.3", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.0.1", + "form-data": "4.0.4", + "formidable": "^3.5.1", "methods": "^1.1.2", - "mime": "^2.5.0", - "qs": "^6.10.1", - "readable-stream": "^3.6.0", - "semver": "^7.3.5" + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" }, "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -23333,13 +21130,13 @@ } }, "supertest": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.2.1.tgz", - "integrity": "sha512-2kBKhfZgnPLmjpzB0n7A2ZnEAWTaLXq4bn3EEVY9w8rUpLyIlSusqKKvWA1Cav7hxXBnXGpxBsSeOHj5wQGe1Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", + "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", "dev": true, "requires": { "methods": "^1.1.2", - "superagent": "^7.0.2" + "superagent": "^9.0.1" } }, "supports-color": { @@ -23351,156 +21148,19 @@ "has-flag": "^3.0.0" } }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.6.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", - "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "peer": true - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true, - "peer": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "peer": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", - "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", + "synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, - "peer": true, "requires": { - "jest-worker": "^27.0.6", - "p-limit": "^3.1.0", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "peer": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "peer": true - } + "@pkgr/core": "^0.2.9" } }, "test-exclude": { @@ -23520,25 +21180,29 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "dependencies": { + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "requires": {} + }, + "picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true + } } }, "tmpl": { @@ -23547,11 +21211,22 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "requires": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } }, "to-regex-range": { "version": "5.0.1", @@ -23567,57 +21242,18 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, - "token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", - "requires": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - } - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "dependencies": { - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - } - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } + "touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true }, - "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, - "requires": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - } + "requires": {} }, "tsconfig-paths": { "version": "3.12.0", @@ -23649,27 +21285,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "tweetnacl": { "version": "1.0.3", @@ -23706,19 +21324,20 @@ "mime-types": "~2.1.24" } }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, + "typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "requires": { - "is-typedarray": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" } }, "typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "peer": true }, @@ -23734,6 +21353,17 @@ "which-boxed-primitive": "^1.0.2" } }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -23762,16 +21392,53 @@ "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", "dev": true }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unraw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz", + "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" + }, + "unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "requires": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1", + "napi-postinstall": "^0.3.0" + } + }, + "update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "requires": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + } }, "uri-js": { "version": "4.4.1", @@ -23782,16 +21449,6 @@ "punycode": "^2.1.0" } }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "url-slug": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/url-slug/-/url-slug-3.0.2.tgz", @@ -23800,39 +21457,34 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==" }, "v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" + "convert-source-map": "^2.0.0" }, "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true } } @@ -23847,33 +21499,15 @@ } }, "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" + "version": "13.15.22", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.22.tgz", + "integrity": "sha512-uT/YQjiyLJP7HSrv/dPZqK9L28xf8hsNca01HSz1dfmI0DgMfjopp1rO/z13NeGF1tVystF0Ejx3y4rUKPw+bQ==" }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -23891,17 +21525,6 @@ "loose-envify": "^1.0.0" } }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "peer": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -23911,100 +21534,11 @@ "defaults": "^1.0.3" } }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "peer": true - }, - "acorn-import-assertions": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", - "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", - "dev": true, - "peer": true, - "requires": {} - } - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "peer": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, "whatwg-fetch": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -24027,6 +21561,20 @@ "is-symbol": "^1.0.3" } }, + "which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + } + }, "word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -24042,32 +21590,17 @@ "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { @@ -24077,23 +21610,23 @@ "dev": true }, "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "requires": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } } }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "requires": {} - }, "x3-linkedlist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/x3-linkedlist/-/x3-linkedlist-1.2.0.tgz", @@ -24110,18 +21643,6 @@ "xtend": "^4.0.0" } }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -24145,38 +21666,31 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "peer": true + "dev": true } } } diff --git a/api/package.json b/api/package.json index 79ea626c1..a2aa3eec1 100644 --- a/api/package.json +++ b/api/package.json @@ -11,8 +11,9 @@ "build": "npm run clean && babel ./src --out-dir dist/src --ignore 'src/**/*.spec.js','src/**/*.test.js' && babel index.js --out-dir dist/", "start": "node ./dist/index.js", "dev": "NODE_OPTIONS=--dns-result-order=ipv4first babel-node index.js", + "dev:watch": "nodemon --exec babel-node index.js", "clean": "rm -rf ./dist && mkdir dist", - "test": "NODE_OPTIONS=--dns-result-order=ipv4first jest --testPathIgnorePatterns=.*-scan-data.*", + "test": "NODE_OPTIONS=--dns-result-order=ipv4first jest --testPathIgnorePatterns=.*-scan-data.* --env=node", "only": "NODE_OPTIONS=--dns-result-order=ipv4first jest", "test-coverage": "jest --coverage", "lint": "eslint src", @@ -21,63 +22,69 @@ "compile": "npx lingui compile", "lin-clean": "npx lingui extract --clean" }, + "overrides": { + "form-data@^3.0.0": "3.0.4", + "form-data@^4.0.0": "4.0.4" + }, "dependencies": { - "@apollo/server": "^4.9.3", - "@lingui/core": "^3.13.0", + "@apollo/server": "^5.5.0", + "@as-integrations/express4": "^1.1.2", + "@lingui/core": "^4.13.0", + "accesscontrol": "^2.2.1", "arango-tools": "^0.6.0", - "arangojs": "^8.0.0", + "arangojs": "^10.2.2", "bcryptjs": "^2.4.3", - "body-parser": "^1.20.2", - "cookie-parser": "^1.4.6", + "body-parser": "^1.20.5", + "compression": "^1.8.1", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", "dataloader": "^2.0.0", "dotenv-safe": "^8.2.0", - "express": "^4.17.3", + "express": "^4.22.2", "express-request-language": "^1.1.15", - "graphql": "^16.8.1", + "graphql": "^16.12.0", "graphql-depth-limit": "^1.1.0", "graphql-redis-subscriptions": "^2.6.0", - "graphql-relay": "^0.10.0", - "graphql-scalars": "^1.22.4", + "graphql-relay": "^0.10.2", + "graphql-scalars": "^1.25.0", "graphql-subscriptions": "^2.0.0", "graphql-validation-complexity": "^0.4.2", "ioredis": "^4.28.3", "isomorphic-fetch": "^3.0.0", - "jsonwebtoken": "^9.0.0", + "jsonwebtoken": "^9.0.2", "make-plural": "^7.1.0", "moment": "^2.29.4", "ms": "^2.1.3", - "nats": "^2.7.0", - "notifications-node-client": "^7.0.3", - "subscriptions-transport-ws": "^0.11.0", + "nats": "^2.18.0", + "notifications-node-client": "^8.2.1", "url-slug": "^3.0.2", - "uuid": "^8.3.2", - "validator": "^13.7.0" + "uuid": "^11.1.1", + "validator": "^13.15.22" }, "devDependencies": { "@babel/cli": "^7.16.8", "@babel/core": "^7.16.7", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.8", - "@jest/test-sequencer": "^27.4.6", - "@lingui/cli": "^3.13.0", - "@lingui/loader": "^3.13.0", - "@lingui/macro": "^3.13.0", + "@jest/test-sequencer": "^30.3.0", + "@lingui/cli": "^5.4.1", + "@lingui/macro": "^4.13.0", "babel-core": "^7.0.0-bridge.0", - "babel-jest": "^27.4.6", + "babel-jest": "^30.3.0", "babel-plugin-macros": "^3.1.0", "babel-polyfill": "^6.26.0", - "eslint": "^7.32.0", + "eslint": "^8.57.1", "eslint-config-prettier": "^8.3.0", - "eslint-config-standard": "^16.0.3", + "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-jest": "^24.7.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.2.0", - "jest": "^27.4.7", + "eslint-plugin-jest": "^29.15.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.6.0", + "jest": "^30.3.0", "jest-fetch-mock": "^3.0.3", - "jest-matcher-utils": "^27.4.6", + "jest-matcher-utils": "^30.3.0", + "nodemon": "^3.1.11", "prettier": "^2.5.1", - "supertest": "^6.2.1" + "supertest": "^7.0.0" } } diff --git a/api/src/__tests__/create-context.test.js b/api/src/__tests__/create-context.test.js index d4d9b39be..0c6f0f6ca 100644 --- a/api/src/__tests__/create-context.test.js +++ b/api/src/__tests__/create-context.test.js @@ -33,5 +33,25 @@ describe('given the create context function', () => { expect(context.userKey).toEqual('1234') }) + + it('returns object with affiliation data source', async () => { + const context = await createContext({ + query: jest.fn(), + transaction: jest.fn(), + collections: [], + req: { + language: 'en', + headers: { + authorization: tokenize({parameters: {userKey: '1234'}}), + }, + }, + res: {}, + }) + + expect(context.dataSources.affiliation).toBeDefined() + expect(context.dataSources.affiliation.byKey).toBeDefined() + expect(context.dataSources.affiliation.connectionsByUserId).toBeDefined() + expect(context.dataSources.affiliation.connectionsByOrgId).toBeDefined() + }) }) }) diff --git a/api/src/__tests__/initialize-loaders.test.js b/api/src/__tests__/initialize-loaders.test.js index f4f13aced..ed28b1dd4 100644 --- a/api/src/__tests__/initialize-loaders.test.js +++ b/api/src/__tests__/initialize-loaders.test.js @@ -1,4 +1,4 @@ -import {initializeLoaders} from '../initialize-loaders' +import { initializeLoaders } from '../initialize-loaders' describe('initializeLoaders', () => { it('returns a object with a key for each loader', () => { @@ -13,9 +13,6 @@ describe('initializeLoaders', () => { }) expect(loaders).toHaveProperty( - 'loadChartSummaryByKey', - 'loadAggregateGuidanceTagByTagId', - 'loadAggregateGuidanceTagConnectionsByTagId', 'loadDkimFailConnectionsBySumId', 'loadDmarcFailConnectionsBySumId', 'loadDmarcSummaryConnectionsByUserId', @@ -25,32 +22,12 @@ describe('initializeLoaders', () => { 'loadSpfFailureConnectionsBySumId', 'loadStartDateFromPeriod', 'loadDmarcYearlySumEdge', - 'loadDomainByDomain', - 'loadDomainByKey', - 'loadDomainConnectionsByOrgId', - 'loadDomainConnectionsByUserId', - 'loadDnsConnectionsByDomainId', - 'loadWebConnectionsByDomainId', - 'loadWebScansByWebId', - 'loadDkimGuidanceTagByTagId', - 'loadDkimGuidanceTagConnectionsByTagId', - 'loadDmarcGuidanceTagByTagId', - 'loadDmarcGuidanceTagConnectionsByTagId', - 'loadHttpsGuidanceTagByTagId', - 'loadHttpsGuidanceTagConnectionsByTagId', - 'loadSpfGuidanceTagByTagId', - 'loadSpfGuidanceTagConnectionsByTagId', - 'loadSslGuidanceTagByTagId', - 'loadSslGuidanceTagConnectionsByTagId', 'loadOrgByKey', - 'loadOrgBySlug', - 'loadOrgConnectionsByDomainId', - 'loadOrgConnectionsByUserId', + 'loadOrganizationNamesById', 'loadUserByUserName', + 'loadUserConnectionsByUserId', 'loadUserByKey', - 'loadAffiliationByKey', - 'loadAffiliationConnectionsByUserId', - 'loadAffiliationConnectionsByOrgId', + 'loadMyTrackerByUserId', 'loadVerifiedDomainsById', 'loadVerifiedDomainByKey', 'loadVerifiedDomainConnections', @@ -59,6 +36,7 @@ describe('initializeLoaders', () => { 'loadVerifiedOrgBySlug', 'loadVerifiedOrgConnectionsByDomainId', 'loadVerifiedOrgConnections', + 'loadAllVerifiedRuaDomains', ) }) }) diff --git a/api/src/__tests__/server.test.js b/api/src/__tests__/server.test.js index 84b94b08a..94f16e71d 100644 --- a/api/src/__tests__/server.test.js +++ b/api/src/__tests__/server.test.js @@ -1,6 +1,7 @@ import request from 'supertest' import { Server } from '../server' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../testUtilities' import dbschema from '../../database.json' const { diff --git a/api/src/access-control.js b/api/src/access-control.js new file mode 100644 index 000000000..195bf5633 --- /dev/null +++ b/api/src/access-control.js @@ -0,0 +1,51 @@ +// Central RBAC definition using accesscontrol +// +// This file defines role-based access control (RBAC) permissions using the AccessControl library. +// +// "Own" refers to resources affiliated with the user's organization. For example, an admin can create domains +// in their affiliated organization, but not in other organizations. This ensures users only manage resources +// within their scope of affiliation. +// +// Roles: +// - user: Basic permissions for managing own CSVs, affiliations, scan requests, and viewing own organization. +// - admin: Extends user. Can manage domains, organizations, logs, tags, and affiliations within their own org. +// - owner: Extends admin. Can delete own organization and create domains. +// - super_admin: Extends owner. Can manage any resource across all organizations. +// +// For maintainability, update permissions here when adding new roles or resources. + +import AccessControl from 'accesscontrol' + +const ac = new AccessControl() + +ac.grant('user').createOwn('csv').readOwn('affiliation').createOwn('scan-request').readOwn('organization') + +ac.grant('admin') + .extend('user') + .createOwn('domain') + .updateOwn('domain', ['*', '!archived']) + .deleteOwn('domain') + .updateOwn('organization', ['*', '!externalId', '!externallyManaged']) + .readOwn('log') + .createOwn('tag') + .updateOwn('tag') + .createOwn('affiliation') + .updateOwn('affiliation') + .deleteOwn('affiliation') + +ac.grant('owner').extend('admin').deleteOwn('organization').createOwn('cvd-enrollment').updateOwn('cvd-enrollment') + +ac.grant('super_admin') + .extend('owner') + .createAny(['organization', 'domain', 'user', 'tag', 'affiliation', 'csv', 'scan-request', 'cvd-enrollment']) + .readAny(['organization', 'domain', 'user', 'tag', 'log', 'affiliation']) + .updateAny(['organization', 'domain', 'user', 'tag', 'affiliation', 'cvd-enrollment']) + .deleteAny(['organization', 'domain', 'user', 'tag', 'affiliation']) + +ac.grant('none') // no permissions — fallback for users with no org affiliation +const _can = ac.can.bind(ac) +ac.can = (role) => _can(role || 'none') + +ac.lock() + +export default ac diff --git a/api/src/additional-findings/data-source.js b/api/src/additional-findings/data-source.js new file mode 100644 index 000000000..e466b4619 --- /dev/null +++ b/api/src/additional-findings/data-source.js @@ -0,0 +1,8 @@ +import { loadAdditionalFindingsByDomainId, loadTop25Reports } from './loaders' + +export class AdditionalFindingsDataSource { + constructor({ query, userKey, i18n, language }) { + this.getByDomainId = loadAdditionalFindingsByDomainId({ query, userKey, i18n }) + this.getTop25Reports = loadTop25Reports({ query, userKey, i18n, language }) + } +} diff --git a/api/src/additional-findings/index.js b/api/src/additional-findings/index.js new file mode 100644 index 000000000..efc2b38f2 --- /dev/null +++ b/api/src/additional-findings/index.js @@ -0,0 +1,4 @@ +export * from './data-source' +export * from './loaders' +export * from './objects' +export * from './input' diff --git a/api/src/additional-findings/input/cvd-enrollment-options.js b/api/src/additional-findings/input/cvd-enrollment-options.js new file mode 100644 index 000000000..6f4945487 --- /dev/null +++ b/api/src/additional-findings/input/cvd-enrollment-options.js @@ -0,0 +1,9 @@ +import { GraphQLInputObjectType } from 'graphql' +import { cvdEnrollmentFields } from '../objects/cvd-enrollment' + +export const CvdEnrollmentInputOptions = new GraphQLInputObjectType({ + name: 'CvdEnrollmenInputOptions', + description: + 'Input options for specifying CVD enrollment details, including program status and CVSS environmental requirements.', + fields: () => ({ ...cvdEnrollmentFields }), +}) diff --git a/api/src/additional-findings/input/index.js b/api/src/additional-findings/input/index.js new file mode 100644 index 000000000..6c8ee0533 --- /dev/null +++ b/api/src/additional-findings/input/index.js @@ -0,0 +1 @@ +export * from './cvd-enrollment-options' diff --git a/api/src/additional-findings/loaders/__tests__/load-additional-findings-by-domain-id.test.js b/api/src/additional-findings/loaders/__tests__/load-additional-findings-by-domain-id.test.js new file mode 100644 index 000000000..7e86d2495 --- /dev/null +++ b/api/src/additional-findings/loaders/__tests__/load-additional-findings-by-domain-id.test.js @@ -0,0 +1,69 @@ +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' + +const { setupI18n } = require('@lingui/core') + +describe('loadAdditionalFindingsByDomainId', () => { + const i18n = setupI18n({ + locale: 'en', + localeData: { + en: {}, + fr: {}, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + + it('throws an error when domainId is not provided', async () => { + const loadAdditionalFindingsByDomainId = + require('../load-additional-findings-by-domain-id').loadAdditionalFindingsByDomainId + const func = loadAdditionalFindingsByDomainId({ query: jest.fn(), userKey: 'userKey', i18n }) + + await expect(func({})).rejects.toThrow("You must provide a `domainId` to retrieve a domain's additional findings.") + }) + + it('throws an error when a database error occurs', async () => { + const query = jest.fn(() => { + throw new Error() + }) + const loadAdditionalFindingsByDomainId = + require('../load-additional-findings-by-domain-id').loadAdditionalFindingsByDomainId + const func = loadAdditionalFindingsByDomainId({ query, userKey: 'userKey', i18n }) + + await expect(func({ domainId: 'domainId' })).rejects.toThrow( + 'Unable to load additional findings. Please try again.', + ) + }) + + it('throws an error when a cursor error occurs', async () => { + const cursor = { + next: jest.fn(() => { + throw new Error() + }), + } + const query = jest.fn(() => cursor) + const loadAdditionalFindingsByDomainId = + require('../load-additional-findings-by-domain-id').loadAdditionalFindingsByDomainId + const func = loadAdditionalFindingsByDomainId({ query, userKey: 'userKey', i18n }) + + await expect(func({ domainId: 'domainId' })).rejects.toThrow( + 'Unable to load additional findings. Please try again.', + ) + }) + + it('returns the finding when everything is correct', async () => { + const finding = { id: 'findingId' } + const cursor = { next: jest.fn(() => finding) } + const query = jest.fn(() => cursor) + const loadAdditionalFindingsByDomainId = + require('../load-additional-findings-by-domain-id').loadAdditionalFindingsByDomainId + const func = loadAdditionalFindingsByDomainId({ query, userKey: 'userKey', i18n }) + + const result = await func({ domainId: 'domainId' }) + + expect(result).toEqual(finding) + }) +}) diff --git a/api/src/additional-findings/loaders/index.js b/api/src/additional-findings/loaders/index.js new file mode 100644 index 000000000..004813d69 --- /dev/null +++ b/api/src/additional-findings/loaders/index.js @@ -0,0 +1,2 @@ +export * from './load-additional-findings-by-domain-id' +export * from './load-top-25-reports' diff --git a/api/src/additional-findings/loaders/load-additional-findings-by-domain-id.js b/api/src/additional-findings/loaders/load-additional-findings-by-domain-id.js new file mode 100644 index 000000000..9e99303ee --- /dev/null +++ b/api/src/additional-findings/loaders/load-additional-findings-by-domain-id.js @@ -0,0 +1,38 @@ +import { t } from '@lingui/macro' + +export const loadAdditionalFindingsByDomainId = + ({ query, userKey, i18n }) => + async ({ domainId }) => { + if (domainId === undefined) { + console.warn(`User: ${userKey} did not set \`domainId\` argument for: loadAdditionalFindingsByDomainId.`) + throw new Error(i18n._(t`You must provide a \`domainId\` to retrieve a domain's additional findings.`)) + } + + let cursor + try { + cursor = await query` + WITH additionalFindings, domains + FOR finding IN additionalFindings + FILTER finding.domain == ${domainId} + LIMIT 1 + RETURN finding + ` + } catch (err) { + console.error( + `Database error occurred while user: ${userKey} was trying to gather additional findings for domain: ${domainId}. Error: ${err}`, + ) + throw new Error(i18n._(t`Unable to load additional findings. Please try again.`)) + } + + let finding + try { + finding = await cursor.next() + } catch (err) { + console.error( + `Cursor error occurred while user: ${userKey} was trying to gather additional findings for domain: ${domainId}. Error: ${err}`, + ) + throw new Error(i18n._(t`Unable to load additional findings. Please try again.`)) + } + + return finding + } diff --git a/api/src/additional-findings/loaders/load-top-25-reports.js b/api/src/additional-findings/loaders/load-top-25-reports.js new file mode 100644 index 000000000..c511a5a9a --- /dev/null +++ b/api/src/additional-findings/loaders/load-top-25-reports.js @@ -0,0 +1,33 @@ +import { t } from '@lingui/macro' + +export const loadTop25Reports = + ({ query, userKey, i18n, language }) => + async () => { + let top25Report + + try { + top25Report = ( + await query` + LET verifiedOrgs = ( + FOR org IN organizations + FILTER org.verified == true + LET orgDetails = TRANSLATE(${language}, org.orgDetails) + RETURN { id: org._id, orgName: orgDetails.name, orgAcronym: orgDetails.acronym } + ) + FOR org IN verifiedOrgs + LET vulnDomainCount = COUNT( + FOR v, e IN 1..1 OUTBOUND org.id claims + OPTIONS { "bfs": true } + FILTER v.cveDetected == true + RETURN v.domain + ) + RETURN MERGE({ assetCount: vulnDomainCount }, org) + ` + ).all() + } catch (err) { + console.error(`Database error occurred when user: ${userKey} running loadTop25Reports: ${err}`) + throw new Error(i18n._(t`Unable to load organization domain statuses. Please try again.`)) + } + + return top25Report + } diff --git a/api/src/additional-findings/objects/__tests__/additonal-finding.test.js b/api/src/additional-findings/objects/__tests__/additonal-finding.test.js new file mode 100644 index 000000000..3d3e0906e --- /dev/null +++ b/api/src/additional-findings/objects/__tests__/additonal-finding.test.js @@ -0,0 +1,114 @@ +import { + additionalFinding, + webConnectionType, + webComponentLocationType, + webComponentPortType, + webComponentCveType, +} from '../additional-finding.js' +import { GraphQLList, GraphQLString } from 'graphql' +import { GraphQLDateTime } from 'graphql-scalars' + +describe('additionalFinding', () => { + it('should have correct fields', () => { + const fields = additionalFinding.getFields() + + expect(fields.timestamp.type).toBe(GraphQLDateTime) + expect(fields.timestamp.description).toBe('The date the finding was discovered.') + + expect(fields.locations.type).toBeInstanceOf(GraphQLList) + expect(fields.locations.description).toBe('The locations the finding was discovered.') + + expect(fields.ports.type).toBeInstanceOf(GraphQLList) + expect(fields.ports.description).toBe('The ports the finding was discovered.') + + expect(fields.headers.type).toBeInstanceOf(GraphQLList) + expect(fields.headers.description).toBe('The headers the finding was discovered.') + + expect(fields.webComponents.type).toBeInstanceOf(GraphQLList) + expect(fields.webComponents.description).toBe('The web components the finding was discovered.') + + expect(fields.vulnerabilities.type).toBeInstanceOf(GraphQLList) + expect(fields.vulnerabilities.description).toBe('The vulnerabilities the finding was discovered.') + }) +}) + +describe('webConnectionType', () => { + it('should have correct fields', () => { + const fields = webConnectionType.getFields() + + expect(fields.webComponentName.type).toBe(GraphQLString) + expect(fields.webComponentName.description).toBe('The URL of the web component.') + + expect(fields.webComponentCategory.type).toBe(GraphQLString) + expect(fields.webComponentCategory.description).toBe('The type of web component.') + + expect(fields.webComponentVersion.type).toBe(GraphQLString) + expect(fields.webComponentVersion.description).toBe('The status of the web component.') + + expect(fields.webComponentCves.type).toBeInstanceOf(GraphQLList) + expect(fields.webComponentPorts.type).toBeInstanceOf(GraphQLList) + + expect(fields.webComponentFirstSeen.type).toBe(GraphQLString) + expect(fields.webComponentLastSeen.type).toBe(GraphQLString) + }) +}) + +describe('webComponentLocationType', () => { + it('should have correct fields', () => { + const fields = webComponentLocationType.getFields() + + expect(fields.region.type).toBe(GraphQLString) + expect(fields.region.description).toBe('The location of the finding.') + + expect(fields.city.type).toBe(GraphQLString) + expect(fields.city.description).toBe('The location of the finding.') + + expect(fields.latitude.type).toBe(GraphQLString) + expect(fields.latitude.description).toBe('The location of the finding.') + + expect(fields.longitude.type).toBe(GraphQLString) + expect(fields.longitude.description).toBe('The location of the finding.') + + expect(fields.firstSeen.type).toBe(GraphQLString) + expect(fields.firstSeen.description).toBe('The location of the finding.') + + expect(fields.lastSeen.type).toBe(GraphQLString) + expect(fields.lastSeen.description).toBe('The location of the finding.') + }) +}) + +describe('webComponentPortType', () => { + it('should have correct fields', () => { + const fields = webComponentPortType.getFields() + + expect(fields.port.type).toBe(GraphQLString) + expect(fields.port.description).toBe('The port the finding was discovered.') + + expect(fields.lastPortState.type).toBe(GraphQLString) + expect(fields.lastPortState.description).toBe('The protocol the finding was discovered.') + + expect(fields.portStateFirstSeen.type).toBe(GraphQLString) + expect(fields.portStateFirstSeen.description).toBe('The date the finding was discovered.') + + expect(fields.portStateLastSeen.type).toBe(GraphQLString) + expect(fields.portStateLastSeen.description).toBe('The date the finding was discovered.') + }) +}) + +describe('webComponentCveType', () => { + it('should have correct fields', () => { + const fields = webComponentCveType.getFields() + + expect(fields.cve.type).toBe(GraphQLString) + expect(fields.cve.description).toBe('The CVE of the finding.') + + expect(fields.cwe.type).toBe(GraphQLString) + expect(fields.cwe.description).toBe('The description of the CVE.') + + expect(fields.cvssScore.type).toBe(GraphQLString) + expect(fields.cvssScore.description).toBe('The severity of the CVE.') + + expect(fields.cvss3Score.type).toBe(GraphQLString) + expect(fields.cvss3Score.description).toBe('The severity of the CVE.') + }) +}) diff --git a/api/src/additional-findings/objects/__tests__/cvd-enrollment.test.js b/api/src/additional-findings/objects/__tests__/cvd-enrollment.test.js new file mode 100644 index 000000000..ebfced89b --- /dev/null +++ b/api/src/additional-findings/objects/__tests__/cvd-enrollment.test.js @@ -0,0 +1,61 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql' +import { EnrollmentStatusEnums, SeverityEnum, CvdRequirementEnums } from '../../../enums' +import { cvdEnrollment } from '../cvd-enrollment' + +describe('cvdEnrollment GraphQLObjectType', () => { + it('should be an instance of GraphQLObjectType', () => { + expect(cvdEnrollment).toBeInstanceOf(GraphQLObjectType) + }) + + it('should have the correct name and description', () => { + expect(cvdEnrollment.name).toBe('CvdEnrollment') + expect(cvdEnrollment.description).toBe( + 'Represents the CVD enrollment details for a domain asset, including enrollment status and CVSS environmental requirements.', + ) + }) + + describe('fields', () => { + const fields = cvdEnrollment.getFields() + + it('should include all expected fields', () => { + expect(fields).toHaveProperty('status') + expect(fields).toHaveProperty('description') + expect(fields).toHaveProperty('maxSeverity') + expect(fields).toHaveProperty('confidentialityRequirement') + expect(fields).toHaveProperty('integrityRequirement') + expect(fields).toHaveProperty('availabilityRequirement') + }) + + it('should have correct type and description for status', () => { + expect(fields.status.type).toBe(EnrollmentStatusEnums) + expect(fields.status.description).toBe( + 'The enrollment status of the asset in the Coordinated Vulnerability Disclosure (CVD) program.', + ) + }) + + it('should have correct type and description for description', () => { + expect(fields.description.type).toBe(GraphQLString) + expect(fields.description.description).toBe('The asset description.') + }) + + it('should have correct type and description for maxSeverity', () => { + expect(fields.maxSeverity.type).toBe(SeverityEnum) + expect(fields.maxSeverity.description).toContain('qualitative rating') + }) + + it('should have correct type and description for confidentialityRequirement', () => { + expect(fields.confidentialityRequirement.type).toBe(CvdRequirementEnums) + expect(fields.confidentialityRequirement.description).toContain('Confidentiality Impact') + }) + + it('should have correct type and description for integrityRequirement', () => { + expect(fields.integrityRequirement.type).toBe(CvdRequirementEnums) + expect(fields.integrityRequirement.description).toContain('Integrity Impact') + }) + + it('should have correct type and description for availabilityRequirement', () => { + expect(fields.availabilityRequirement.type).toBe(CvdRequirementEnums) + expect(fields.availabilityRequirement.description).toContain('Availability Impact') + }) + }) +}) diff --git a/api/src/additional-findings/objects/additional-finding.js b/api/src/additional-findings/objects/additional-finding.js new file mode 100644 index 000000000..e842a0183 --- /dev/null +++ b/api/src/additional-findings/objects/additional-finding.js @@ -0,0 +1,196 @@ +import { GraphQLObjectType, GraphQLList, GraphQLString } from 'graphql' +import { GraphQLDateTime } from 'graphql-scalars' + +export const additionalFinding = new GraphQLObjectType({ + name: 'AdditionalFinding', + fields: () => ({ + timestamp: { + type: GraphQLDateTime, + description: `The date the finding was discovered.`, + resolve: ({ timestamp }) => new Date(timestamp), + }, + locations: { + type: new GraphQLList(webComponentLocationType), + description: `The locations the finding was discovered.`, + resolve: ({ locations }) => locations, + }, + ports: { + type: new GraphQLList(webComponentPortType), + description: `The ports the finding was discovered.`, + resolve: ({ ports }) => ports, + }, + headers: { + type: new GraphQLList(GraphQLString), + description: `The headers the finding was discovered.`, + resolve: ({ headers }) => headers, + }, + webComponents: { + type: new GraphQLList(webConnectionType), + description: `The web components the finding was discovered.`, + resolve: ({ webComponents }) => webComponents, + }, + vulnerabilities: { + type: new GraphQLList(webComponentCveType), + description: `The vulnerabilities the finding was discovered.`, + resolve: ({ webComponents }) => { + const vulnerabilities = [] + for (const webComponent of webComponents) { + vulnerabilities.push(...webComponent.WebComponentCves) + } + + const jsonObject = vulnerabilities.map(JSON.stringify) + const uniqueSet = new Set(jsonObject) + const uniqueVulns = Array.from(uniqueSet).map(JSON.parse) + uniqueVulns.sort((a, b) => Number(a.Cvss3Score) - Number(b.Cvss3Score)) + + return uniqueVulns + }, + }, + }), + description: `A finding imported from an external ASM tool.`, +}) + +export const webConnectionType = new GraphQLObjectType({ + name: 'WebConnectionType', + fields: () => ({ + webComponentName: { + type: GraphQLString, + description: `The URL of the web component.`, + resolve: ({ WebComponentName }) => WebComponentName, + }, + webComponentCategory: { + type: GraphQLString, + description: `The type of web component.`, + resolve: ({ WebComponentCategory }) => WebComponentCategory, + }, + webComponentVersion: { + type: GraphQLString, + description: `The status of the web component.`, + resolve: ({ WebComponentVersion }) => WebComponentVersion, + }, + webComponentCves: { + type: new GraphQLList(webComponentCveType), + description: '', + resolve: ({ WebComponentCves }) => WebComponentCves, + }, + webComponentPorts: { + type: new GraphQLList(webComponentPortType), + description: '', + resolve: ({ WebComponentPorts }) => WebComponentPorts, + }, + webComponentFirstSeen: { + type: GraphQLString, + description: '', + resolve: ({ WebComponentFirstSeen }) => WebComponentFirstSeen, + }, + webComponentLastSeen: { + type: GraphQLString, + description: '', + resolve: ({ WebComponentLastSeen }) => WebComponentLastSeen, + }, + }), +}) + +export const webComponentLocationType = new GraphQLObjectType({ + name: 'WebComponentLocation', + fields: () => ({ + region: { + type: GraphQLString, + description: `The location of the finding.`, + resolve: ({ Region }) => Region, + }, + city: { + type: GraphQLString, + description: `The location of the finding.`, + resolve: ({ City }) => City, + }, + latitude: { + type: GraphQLString, + description: `The location of the finding.`, + resolve: ({ Latitude }) => Latitude, + }, + longitude: { + type: GraphQLString, + description: `The location of the finding.`, + resolve: ({ Longitude }) => Longitude, + }, + firstSeen: { + type: GraphQLString, + description: `The location of the finding.`, + resolve: ({ FirstSeen }) => FirstSeen, + }, + lastSeen: { + type: GraphQLString, + description: `The location of the finding.`, + resolve: ({ LastSeen }) => LastSeen, + }, + }), +}) + +export const webComponentPortType = new GraphQLObjectType({ + name: 'WebComponentPort', + fields: () => ({ + port: { + type: GraphQLString, + description: `The port the finding was discovered.`, + resolve: ({ Port }) => Port, + }, + lastPortState: { + type: GraphQLString, + description: `The protocol the finding was discovered.`, + resolve: ({ LastPortState }) => LastPortState, + }, + portStateFirstSeen: { + type: GraphQLString, + description: `The date the finding was discovered.`, + resolve: ({ PortStateFirstSeen }) => PortStateFirstSeen, + }, + portStateLastSeen: { + type: GraphQLString, + description: `The date the finding was discovered.`, + resolve: ({ PortStateLastSeen }) => PortStateLastSeen, + }, + }), +}) + +export const webComponentCveType = new GraphQLObjectType({ + name: 'WebComponentCVE', + fields: () => ({ + cve: { + type: GraphQLString, + description: `The CVE of the finding.`, + resolve: ({ Cve }) => Cve, + }, + cwe: { + type: GraphQLString, + description: `The description of the CVE.`, + resolve: ({ Cwe }) => Cwe, + }, + cvssScore: { + type: GraphQLString, + description: `The severity of the CVE.`, + resolve: ({ CvssScore }) => CvssScore, + }, + cvss3Score: { + type: GraphQLString, + description: `The severity of the CVE.`, + resolve: ({ Cvss3Score }) => Number(Cvss3Score).toFixed(1), + }, + severity: { + type: GraphQLString, + description: `The severity of the CVE.`, + resolve: ({ Cvss3Score }) => { + const score = Number(Cvss3Score) + if (score >= 9) return 'critical' + else if (score >= 7 && score < 9) return 'high' + else if (score >= 4 && score < 7) return 'medium' + else return 'low' + }, + }, + confidenceLevel: { + type: GraphQLString, + description: 'Level of confidence that finding is accurate.', + resolve: ({ ConfidenceLevel }) => ConfidenceLevel, + }, + }), +}) diff --git a/api/src/additional-findings/objects/cvd-enrollment.js b/api/src/additional-findings/objects/cvd-enrollment.js new file mode 100644 index 000000000..4cbb0d4ca --- /dev/null +++ b/api/src/additional-findings/objects/cvd-enrollment.js @@ -0,0 +1,38 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql' +import { EnrollmentStatusEnums, SeverityEnum, CvdRequirementEnums } from '../../enums' + +export const cvdEnrollmentFields = { + status: { + description: 'The enrollment status of the asset in the Coordinated Vulnerability Disclosure (CVD) program.', + type: EnrollmentStatusEnums, + }, + description: { + description: 'The asset description.', + type: GraphQLString, + }, + maxSeverity: { + description: + 'The qualitative rating of the maximum severity allowed on this asset. Its value is calculated from the combination of all three of the environmental requirements (CR, IR, and AR).', + type: SeverityEnum, + }, + confidentialityRequirement: { + description: + 'A CVSS environmental modifier that reweights Confidentiality Impact of a vulnerability on this asset.', + type: CvdRequirementEnums, + }, + integrityRequirement: { + description: 'A CVSS environmental modifier that reweights Integrity Impact of a vulnerability on this asset.', + type: CvdRequirementEnums, + }, + availabilityRequirement: { + description: 'A CVSS environmental modifier that reweights Availability Impact of a vulnerability on this asset.', + type: CvdRequirementEnums, + }, +} + +export const cvdEnrollment = new GraphQLObjectType({ + name: 'CvdEnrollment', + description: + 'Represents the CVD enrollment details for a domain asset, including enrollment status and CVSS environmental requirements.', + fields: () => ({ ...cvdEnrollmentFields }), +}) diff --git a/api/src/additional-findings/objects/index.js b/api/src/additional-findings/objects/index.js new file mode 100644 index 000000000..18e5dd687 --- /dev/null +++ b/api/src/additional-findings/objects/index.js @@ -0,0 +1,2 @@ +export * from './additional-finding' +export * from './cvd-enrollment' diff --git a/api/src/additional-findings/queries/__tests__/get-top-25-reports.test.js b/api/src/additional-findings/queries/__tests__/get-top-25-reports.test.js new file mode 100644 index 000000000..3ecc2ee53 --- /dev/null +++ b/api/src/additional-findings/queries/__tests__/get-top-25-reports.test.js @@ -0,0 +1,435 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql, GraphQLSchema, GraphQLError } from 'graphql' + +import { createQuerySchema } from '../../../query' +import { createMutationSchema } from '../../../mutation' +import { checkSuperAdmin, superAdminRequired, userRequired, verifiedRequired } from '../../../auth' +import { loadUserByKey } from '../../../user/loaders' +import { loadTop25Reports } from '../../loaders' +import dbschema from '../../../../database.json' +import { setupI18n } from '@lingui/core' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' + +const { DB_PASS: rootPass, DB_URL: url } = process.env + +describe('given getTop25Reports', () => { + // eslint-disable-next-line no-unused-vars + let query, drop, truncate, schema, collections, superAdminOrg, domainOne, domainTwo, orgOne, orgTwo, i18n, user + + const consoleOutput = [] + const mockedInfo = (output) => consoleOutput.push(output) + const mockedWarn = (output) => consoleOutput.push(output) + const mockedError = (output) => consoleOutput.push(output) + beforeAll(async () => { + // Create GQL Schema + schema = new GraphQLSchema({ + query: createQuerySchema(), + mutation: createMutationSchema(), + }) + }) + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + beforeAll(async () => { + // Generate DB Items + ;({ query, drop, truncate, collections } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) + }) + beforeEach(async () => { + console.info = mockedInfo + console.warn = mockedWarn + console.error = mockedError + consoleOutput.length = 0 + }) + beforeEach(async () => { + user = await collections.users.save({ + displayName: 'Test Account', + userName: 'test.account@istio.actually.exists', + emailValidated: true, + }) + superAdminOrg = await collections.organizations.save({ + orgDetails: { + en: { + slug: 'super-admin', + acronym: 'SA', + name: 'Super Admin', + zone: 'NFED', + sector: 'NTBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'super-admin', + acronym: 'SA', + name: 'Super Admin', + zone: 'NPFED', + sector: 'NPTBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + + orgOne = await collections.organizations.save({ + verified: true, + orgDetails: { + en: { + slug: 'org-one', + acronym: 'OO', + name: 'Org One', + zone: 'NFED', + sector: 'NTBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'org-one', + acronym: 'OO', + name: 'Org One', + zone: 'NPFED', + sector: 'NPTBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + + orgTwo = await collections.organizations.save({ + verified: true, + orgDetails: { + en: { + slug: 'org-two', + acronym: 'OT', + name: 'Org Two', + zone: 'NFED', + sector: 'NTBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'org-two', + acronym: 'OT', + name: 'Org Two', + zone: 'NPFED', + sector: 'NPTBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + + domainOne = await collections.domains.save({ + domain: 'domain.one', + status: { + https: 'fail', + hsts: 'pass', + certificates: 'pass', + ciphers: 'pass', + curves: 'pass', + protocols: 'pass', + spf: 'pass', + dkim: 'pass', + dmarc: 'pass', + }, + rcode: 'NOERROR', + blocked: false, + wildcardSibling: false, + hasEntrustCertificate: false, + cveDetected: true, + }) + domainTwo = await collections.domains.save({ + domain: 'domain.two', + status: { + https: 'pass', + hsts: 'fail', + certificates: 'pass', + ciphers: 'fail', + curves: 'pass', + protocols: 'fail', + spf: 'pass', + dkim: 'pass', + dmarc: 'fail', + }, + rcode: 'NOERROR', + blocked: false, + wildcardSibling: false, + hasEntrustCertificate: false, + cveDetected: true, + }) + + await collections.claims.save({ + _from: orgOne._id, + _to: domainOne._id, + }) + await collections.claims.save({ + _from: orgTwo._id, + _to: domainTwo._id, + }) + }) + afterEach(async () => { + await truncate() + }) + afterAll(async () => { + await drop() + }) + let loginRequiredBool + describe('login is not required', () => { + beforeEach(async () => { + loginRequiredBool = false + }) + describe('the user is not a super admin', () => { + it('returns a permission error', async () => { + const response = await graphql({ + schema, + source: ` + query { + getTop25Reports + } + `, + rootValue: null, + contextValue: { + i18n, + userKey: user._key, + language: 'en', + auth: { + checkSuperAdmin: checkSuperAdmin({ + i18n, + userKey: user._key, + query, + }), + userRequired: userRequired({ + i18n, + userKey: user._key, + loadUserByKey: loadUserByKey({ + query, + userKey: user._key, + i18n, + }), + }), + verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), + loginRequiredBool: loginRequiredBool, + }, + dataSources: { + additionalFindings: { + getTop25Reports: loadTop25Reports({ query, userKey: user._key, i18n, language: 'en' }), + }, + }, + }, + }) + const error = [ + new GraphQLError('Permissions error. You do not have sufficient permissions to access this data.'), + ] + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `User: ${user._key} attempted to access controlled functionality without sufficient privileges.`, + ]) + }) + }) + describe('the user is a super admin', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: superAdminOrg._id, + _to: user._id, + permission: 'super_admin', + }) + }) + + it('returns all domain status results', async () => { + const response = await graphql({ + schema, + source: ` + query { + getTop25Reports + } + `, + rootValue: null, + contextValue: { + i18n, + language: 'en', + userKey: user._key, + auth: { + checkSuperAdmin: checkSuperAdmin({ + i18n, + userKey: user._key, + query, + }), + userRequired: userRequired({ + i18n, + userKey: user._key, + loadUserByKey: loadUserByKey({ + query, + userKey: user._key, + i18n, + }), + }), + verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), + loginRequiredBool: loginRequiredBool, + }, + dataSources: { + additionalFindings: { + getTop25Reports: loadTop25Reports({ query, userKey: user._key, i18n, language: 'en' }), + }, + }, + }, + }) + + const expectedResponse = { + data: { + getTop25Reports: `orgName,orgAcronym,assetCount +"Org One","OO","1" +"Org Two","OT","1" +Government of Canada,GC,2`, + }, + } + + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([`User ${user._key} successfully retrieved all top 25 reports.`]) + }) + }) + }) + describe('login is required', () => { + beforeEach(async () => { + loginRequiredBool = true + }) + describe('the user is not a super admin', () => { + it('returns a permission error', async () => { + const response = await graphql({ + schema, + source: ` + query { + getTop25Reports + } + `, + rootValue: null, + contextValue: { + i18n, + language: 'en', + userKey: user._key, + auth: { + checkSuperAdmin: checkSuperAdmin({ + i18n, + userKey: user._key, + query, + }), + userRequired: userRequired({ + i18n, + userKey: user._key, + loadUserByKey: loadUserByKey({ + query, + userKey: user._key, + i18n, + }), + }), + verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), + loginRequiredBool: loginRequiredBool, + }, + dataSources: { + additionalFindings: { + getTop25Reports: loadTop25Reports({ query, userKey: user._key, i18n, language: 'en' }), + }, + }, + }, + }) + const error = [ + new GraphQLError('Permissions error. You do not have sufficient permissions to access this data.'), + ] + + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `User: ${user._key} attempted to access controlled functionality without sufficient privileges.`, + ]) + }) + }) + describe('the user is a super admin', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: superAdminOrg._id, + _to: user._id, + permission: 'super_admin', + }) + }) + + it('returns all domain status results', async () => { + const response = await graphql({ + schema, + source: ` + query { + getTop25Reports + } + `, + rootValue: null, + contextValue: { + i18n, + language: 'en', + userKey: user._key, + auth: { + checkSuperAdmin: checkSuperAdmin({ + i18n, + userKey: user._key, + query, + }), + userRequired: userRequired({ + i18n, + userKey: user._key, + loadUserByKey: loadUserByKey({ + query, + userKey: user._key, + i18n, + }), + }), + verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), + loginRequiredBool: loginRequiredBool, + }, + dataSources: { + additionalFindings: { + getTop25Reports: loadTop25Reports({ query, userKey: user._key, i18n, language: 'en' }), + }, + }, + }, + }) + const expectedResponse = { + data: { + getTop25Reports: `orgName,orgAcronym,assetCount +"Org One","OO","1" +"Org Two","OT","1" +Government of Canada,GC,2`, + }, + } + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([`User ${user._key} successfully retrieved all top 25 reports.`]) + }) + }) + }) +}) diff --git a/api/src/additional-findings/queries/get-top-25-report.js b/api/src/additional-findings/queries/get-top-25-report.js new file mode 100644 index 000000000..ec770e141 --- /dev/null +++ b/api/src/additional-findings/queries/get-top-25-report.js @@ -0,0 +1,45 @@ +import { GraphQLString } from 'graphql' + +export const getTop25Reports = { + type: GraphQLString, + description: 'CSV formatted output of top 25 reports.', + resolve: async ( + _, + args, + { + userKey, + auth: { checkSuperAdmin, userRequired, verifiedRequired, superAdminRequired }, + dataSources: { additionalFindings }, + language, + }, + ) => { + const user = await userRequired() + verifiedRequired({ user }) + + const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ user, isSuperAdmin }) + + const top25Reports = await additionalFindings.getTop25Reports({ ...args }) + + console.info(`User ${userKey} successfully retrieved all top 25 reports.`) + + if (top25Reports === undefined) return top25Reports + + const headers = ['orgName', 'orgAcronym', 'assetCount'] + let csvOutput = headers.join(',') + let totalAssetCount = 0 + top25Reports.forEach((domainStatus) => { + const csvLine = headers + .map((header) => { + return `"${domainStatus[header]}"` + }) + .join(',') + csvOutput += `\n${csvLine}` + totalAssetCount += domainStatus.assetCount + }) + const govName = language === 'en' ? 'Government of Canada' : 'Gouvernement du Canada' + csvOutput += `\n${govName},GC,${totalAssetCount}` + + return csvOutput + }, +} diff --git a/api/src/additional-findings/queries/index.js b/api/src/additional-findings/queries/index.js new file mode 100644 index 000000000..8542ff42c --- /dev/null +++ b/api/src/additional-findings/queries/index.js @@ -0,0 +1 @@ +export * from './get-top-25-report' diff --git a/api/src/affiliation/data-source.js b/api/src/affiliation/data-source.js new file mode 100644 index 000000000..b6ec488fd --- /dev/null +++ b/api/src/affiliation/data-source.js @@ -0,0 +1,227 @@ +import { + loadAffiliationByKey, + loadAffiliationConnectionsByOrgId, + loadAffiliationConnectionsByUserId, +} from './loaders' + +export class AffiliationDataSource { + constructor({ query, userKey, i18n, language, cleanseInput, transaction, collections }) { + this._query = query + this._transaction = transaction + this._collections = collections + + this.byKey = loadAffiliationByKey({ query, userKey, i18n }) + this.connectionsByUserId = loadAffiliationConnectionsByUserId({ + query, + language, + userKey, + cleanseInput, + i18n, + }) + this.connectionsByOrgId = loadAffiliationConnectionsByOrgId({ + query, + userKey, + cleanseInput, + i18n, + }) + } + + async affiliationByOrgAndUser({ orgId, userId }) { + let affiliationCursor + try { + affiliationCursor = await this._query` + WITH affiliations, organizations, users + FOR v, e IN 1..1 OUTBOUND ${orgId} affiliations + FILTER e._to == ${userId} + RETURN e + ` + } catch (err) { + err.affiliationDataSourceOp = 'query' + throw err + } + + if (affiliationCursor.count < 1) { + return undefined + } + + try { + return await affiliationCursor.next() + } catch (err) { + err.affiliationDataSourceOp = 'cursor' + throw err + } + } + + async createAffiliation({ orgId, userId, permission }) { + const transaction = await this._transaction(this._collections) + + try { + await transaction.step( + () => + this._query` + WITH affiliations, organizations, users + INSERT { + _from: ${orgId}, + _to: ${userId}, + permission: ${permission}, + } INTO affiliations + `, + ) + } catch (err) { + if (typeof transaction.abort === 'function') await transaction.abort() + err.affiliationDataSourceOp = 'trx-step' + throw err + } + + try { + await transaction.commit() + } catch (err) { + if (typeof transaction.abort === 'function') await transaction.abort() + err.affiliationDataSourceOp = 'trx-commit' + throw err + } + } + + async createPendingAffiliation({ orgId, userId }) { + return this.createAffiliation({ orgId, userId, permission: 'pending' }) + } + + async updateAffiliationPermission({ affiliationKey, orgId, userId, permission }) { + const trx = await this._transaction(this._collections) + + const edge = { + _from: orgId, + _to: userId, + permission, + } + + try { + await trx.step( + async () => + await this._query` + WITH affiliations, organizations, users + UPSERT { _key: ${affiliationKey} } + INSERT ${edge} + UPDATE ${edge} + IN affiliations + `, + ) + } catch (err) { + if (typeof trx.abort === 'function') await trx.abort() + err.affiliationDataSourceOp = 'trx-step' + throw err + } + + try { + await trx.commit() + } catch (err) { + if (typeof trx.abort === 'function') await trx.abort() + err.affiliationDataSourceOp = 'trx-commit' + throw err + } + } + + async removeAffiliation({ orgId, userId }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => + this._query` + WITH affiliations, organizations, users + FOR aff IN affiliations + FILTER aff._from == ${orgId} + FILTER aff._to == ${userId} + REMOVE aff IN affiliations + RETURN true + `, + ) + } catch (err) { + if (typeof trx.abort === 'function') await trx.abort() + err.affiliationDataSourceOp = 'trx-step' + throw err + } + + try { + await trx.commit() + } catch (err) { + if (typeof trx.abort === 'function') await trx.abort() + err.affiliationDataSourceOp = 'trx-commit' + throw err + } + } + + async transferOrgOwnership({ orgId, fromUserId, toUserId }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => + this._query` + WITH affiliations, organizations, users + FOR aff IN affiliations + FILTER aff._from == ${orgId} + FILTER aff._to == ${fromUserId} + UPDATE { _key: aff._key } WITH { + permission: "admin", + } IN affiliations + RETURN aff + `, + ) + } catch (err) { + if (typeof trx.abort === 'function') await trx.abort() + err.affiliationDataSourceOp = 'trx-step' + throw err + } + + try { + await trx.step( + () => + this._query` + WITH affiliations, organizations, users + FOR aff IN affiliations + FILTER aff._from == ${orgId} + FILTER aff._to == ${toUserId} + UPDATE { _key: aff._key } WITH { + permission: "owner", + } IN affiliations + RETURN aff + `, + ) + } catch (err) { + if (typeof trx.abort === 'function') await trx.abort() + err.affiliationDataSourceOp = 'trx-step' + throw err + } + + try { + await trx.commit() + } catch (err) { + if (typeof trx.abort === 'function') await trx.abort() + err.affiliationDataSourceOp = 'trx-commit' + throw err + } + } + + async orgAdminUserKeys({ orgId }) { + let orgAdminsCursor + try { + orgAdminsCursor = await this._query` + WITH affiliations, organizations, users + FOR v, e IN 1..1 OUTBOUND ${orgId} affiliations + FILTER e.permission IN ["admin", "owner", "super_admin"] + RETURN v._key + ` + } catch (err) { + err.affiliationDataSourceOp = 'query' + throw err + } + + try { + return await orgAdminsCursor.all() + } catch (err) { + err.affiliationDataSourceOp = 'cursor' + throw err + } + } +} diff --git a/api/src/affiliation/index.js b/api/src/affiliation/index.js index 8c7cd352a..afb08ea1a 100644 --- a/api/src/affiliation/index.js +++ b/api/src/affiliation/index.js @@ -1,3 +1,4 @@ +export * from './data-source' export * from './loaders' export * from './mutations' export * from './objects' diff --git a/api/src/affiliation/loaders/__tests__/load-affiliation-by-key.test.js b/api/src/affiliation/loaders/__tests__/load-affiliation-by-key.test.js index cb0b74694..d6649a47e 100644 --- a/api/src/affiliation/loaders/__tests__/load-affiliation-by-key.test.js +++ b/api/src/affiliation/loaders/__tests__/load-affiliation-by-key.test.js @@ -1,12 +1,13 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {loadAffiliationByKey} from '..' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { loadAffiliationByKey } from '..' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadAffiliationByKey dataloader', () => { let i18n @@ -24,7 +25,7 @@ describe('given a loadAffiliationByKey dataloader', () => { let query, drop, truncate, collections, orgOne, orgTwo, affOne, user beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -41,7 +42,6 @@ describe('given a loadAffiliationByKey dataloader', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -125,7 +125,7 @@ describe('given a loadAffiliationByKey dataloader', () => { ` const expectedAffiliation = await expectedCursor.next() - const loader = loadAffiliationByKey({query, i18n}) + const loader = loadAffiliationByKey({ query, i18n }) const affiliation = await loader.load(expectedAffiliation._key) expect(affiliation).toEqual(expectedAffiliation) @@ -148,7 +148,7 @@ describe('given a loadAffiliationByKey dataloader', () => { expectedAffiliations.push(tempAff) } - const loader = loadAffiliationByKey({query, i18n}) + const loader = loadAffiliationByKey({ query, i18n }) const affiliations = await loader.loadMany(affiliationIds) expect(affiliations).toEqual(expectedAffiliations) }) @@ -160,8 +160,8 @@ describe('given a loadAffiliationByKey dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -172,9 +172,7 @@ describe('given a loadAffiliationByKey dataloader', () => { }) describe('database error is raised', () => { it('throws an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadAffiliationByKey({ query: mockedQuery, userKey: '1234', @@ -184,9 +182,7 @@ describe('given a loadAffiliationByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to find user affiliation(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to find user affiliation(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -211,9 +207,7 @@ describe('given a loadAffiliationByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to find user affiliation(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to find user affiliation(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -227,8 +221,8 @@ describe('given a loadAffiliationByKey dataloader', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -239,9 +233,7 @@ describe('given a loadAffiliationByKey dataloader', () => { }) describe('database error is raised', () => { it('throws an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadAffiliationByKey({ query: mockedQuery, userKey: '1234', @@ -252,9 +244,7 @@ describe('given a loadAffiliationByKey dataloader', () => { await loader.load('1') } catch (err) { expect(err).toEqual( - new Error( - `Impossible de trouver l'affiliation de l'utilisateur (s). Veuillez réessayer.`, - ), + new Error(`Impossible de trouver l'affiliation de l'utilisateur (s). Veuillez réessayer.`), ) } @@ -281,9 +271,7 @@ describe('given a loadAffiliationByKey dataloader', () => { await loader.load('1') } catch (err) { expect(err).toEqual( - new Error( - `Impossible de trouver l'affiliation de l'utilisateur (s). Veuillez réessayer.`, - ), + new Error(`Impossible de trouver l'affiliation de l'utilisateur (s). Veuillez réessayer.`), ) } diff --git a/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-org-id.test.js b/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-org-id.test.js index 3fa581cac..b303e6959 100644 --- a/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-org-id.test.js +++ b/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-org-id.test.js @@ -1,21 +1,19 @@ -import {stringify} from 'jest-matcher-utils' -import {ensure, dbNameFromFile} from 'arango-tools' -import {toGlobalId} from 'graphql-relay' -import {setupI18n} from '@lingui/core' +import { stringify } from 'jest-matcher-utils' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { toGlobalId } from 'graphql-relay' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' -import {cleanseInput} from '../../../validators' -import { - loadAffiliationConnectionsByOrgId, - loadAffiliationByKey, -} from '../index' +import { cleanseInput } from '../../../validators' +import { loadAffiliationConnectionsByOrgId, loadAffiliationByKey } from '../index' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the load affiliations by org id function', () => { - let query, drop, truncate, collections, user, org, userTwo, i18n + let query, drop, truncate, collections, db, user, org, userTwo, i18n const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -30,7 +28,7 @@ describe('given the load affiliations by org id function', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections, db } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -46,14 +44,12 @@ describe('given the load affiliations by org id function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) userTwo = await collections.users.save({ userName: 'test.accounttwo@istio.actually.exists', displayName: 'Jane Doe', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -122,11 +118,8 @@ describe('given the load affiliations by org id function', () => { i18n, }) - const affLoader = loadAffiliationByKey({query}) - const expectedAffiliations = await affLoader.loadMany([ - affOne._key, - affTwo._key, - ]) + const affLoader = loadAffiliationByKey({ query }) + const expectedAffiliations = await affLoader.loadMany([affOne._key, affTwo._key]) expectedAffiliations[0].id = expectedAffiliations[0]._key expectedAffiliations[1].id = expectedAffiliations[1]._key @@ -153,14 +146,8 @@ describe('given the load affiliations by org id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), }, } @@ -176,11 +163,8 @@ describe('given the load affiliations by org id function', () => { i18n, }) - const affLoader = loadAffiliationByKey({query}) - const expectedAffiliations = await affLoader.loadMany([ - affOne._key, - affTwo._key, - ]) + const affLoader = loadAffiliationByKey({ query }) + const expectedAffiliations = await affLoader.loadMany([affOne._key, affTwo._key]) expectedAffiliations[0].id = expectedAffiliations[0]._key expectedAffiliations[1].id = expectedAffiliations[1]._key @@ -197,10 +181,7 @@ describe('given the load affiliations by org id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -210,14 +191,8 @@ describe('given the load affiliations by org id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -233,11 +208,8 @@ describe('given the load affiliations by org id function', () => { i18n, }) - const affLoader = loadAffiliationByKey({query}) - const expectedAffiliations = await affLoader.loadMany([ - affOne._key, - affTwo._key, - ]) + const affLoader = loadAffiliationByKey({ query }) + const expectedAffiliations = await affLoader.loadMany([affOne._key, affTwo._key]) expectedAffiliations[0].id = expectedAffiliations[0]._key expectedAffiliations[1].id = expectedAffiliations[1]._key @@ -253,10 +225,7 @@ describe('given the load affiliations by org id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -266,14 +235,8 @@ describe('given the load affiliations by org id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -289,11 +252,8 @@ describe('given the load affiliations by org id function', () => { i18n, }) - const affLoader = loadAffiliationByKey({query}) - const expectedAffiliations = await affLoader.loadMany([ - affOne._key, - affTwo._key, - ]) + const affLoader = loadAffiliationByKey({ query }) + const expectedAffiliations = await affLoader.loadMany([affOne._key, affTwo._key]) expectedAffiliations[0].id = expectedAffiliations[0]._key expectedAffiliations[1].id = expectedAffiliations[1]._key @@ -309,10 +269,7 @@ describe('given the load affiliations by org id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[1]._key), node: { ...expectedAffiliations[1], }, @@ -322,14 +279,8 @@ describe('given the load affiliations by org id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), }, } @@ -355,11 +306,8 @@ describe('given the load affiliations by org id function', () => { i18n, }) - const affLoader = loadAffiliationByKey({query}) - const expectedAffiliations = await affLoader.loadMany([ - affOne._key, - affTwo._key, - ]) + const affLoader = loadAffiliationByKey({ query }) + const expectedAffiliations = await affLoader.loadMany([affOne._key, affTwo._key]) expectedAffiliations[0].id = expectedAffiliations[0]._key expectedAffiliations[1].id = expectedAffiliations[1]._key @@ -376,27 +324,18 @@ describe('given the load affiliations by org id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, }, ], - totalCount: 2, + totalCount: 1, pageInfo: { - hasNextPage: true, + hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -412,11 +351,8 @@ describe('given the load affiliations by org id function', () => { i18n, }) - const affLoader = loadAffiliationByKey({query}) - const expectedAffiliations = await affLoader.loadMany([ - affOne._key, - affTwo._key, - ]) + const affLoader = loadAffiliationByKey({ query }) + const expectedAffiliations = await affLoader.loadMany([affOne._key, affTwo._key]) expectedAffiliations[0].id = expectedAffiliations[0]._key expectedAffiliations[1].id = expectedAffiliations[1]._key @@ -433,27 +369,18 @@ describe('given the load affiliations by org id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, }, ], - totalCount: 2, + totalCount: 1, pageInfo: { - hasNextPage: true, + hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -476,18 +403,6 @@ describe('given the load affiliations by org id function', () => { }) orgOne = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, orgDetails: { en: { slug: 'slug-org-a', @@ -511,6 +426,12 @@ describe('given the load affiliations by org id function', () => { }, }, }) + const orgOneSummary = await collections.organizationSummaries.save({ + organization: orgOne._id, + web: { pass: 50, fail: 1000, total: 1050 }, + mail: { pass: 50, fail: 1000, total: 1050 }, + }) + await db.collection('organizations').update(orgOne._key, { latestSummaryId: orgOneSummary._id }) affOne = await collections.affiliations.save({ _key: '1', _from: orgOne._id, @@ -530,7 +451,7 @@ describe('given the load affiliations by org id function', () => { permission: 'user', }) }) - describe('ordering by USER_USERNAME', () => { + describe('ordering by USERNAME', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { const expectedAffiliation = await loadAffiliationByKey({ @@ -552,7 +473,7 @@ describe('given the load affiliations by org id function', () => { after: toGlobalId('affiliation', affOne._key), before: toGlobalId('affiliation', affThree._key), orderBy: { - field: 'user-username', + field: 'username', direction: 'ASC', }, } @@ -601,7 +522,7 @@ describe('given the load affiliations by org id function', () => { after: toGlobalId('affiliation', affThree._key), before: toGlobalId('affiliation', affOne._key), orderBy: { - field: 'user-username', + field: 'username', direction: 'DESC', }, } @@ -669,8 +590,8 @@ describe('given the load affiliations by org id function', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en'], messages: { @@ -693,12 +614,10 @@ describe('given the load affiliations by org id function', () => { last: 1, } try { - await affiliationLoader({orgId: org._id, ...connectionArgs}) + await affiliationLoader({ orgId: org._id, ...connectionArgs }) } catch (err) { expect(err).toEqual( - new Error( - 'Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.', - ), + new Error('Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.'), ) } @@ -755,11 +674,7 @@ describe('given the load affiliations by org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `Affiliation` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `Affiliation` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadAffiliationConnectionsByOrgId.`, @@ -785,11 +700,7 @@ describe('given the load affiliations by org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `Affiliation` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `Affiliation` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadAffiliationConnectionsByOrgId.`, @@ -862,9 +773,7 @@ describe('given the load affiliations by org id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const affiliationLoader = loadAffiliationConnectionsByOrgId({ query, userKey: user._key, @@ -881,11 +790,7 @@ describe('given the load affiliations by org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -897,9 +802,7 @@ describe('given the load affiliations by org id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const affiliationLoader = loadAffiliationConnectionsByOrgId({ query, userKey: user._key, @@ -916,11 +819,7 @@ describe('given the load affiliations by org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -935,11 +834,7 @@ describe('given the load affiliations by org id function', () => { describe('given a database error', () => { describe('while querying affiliations', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue( - new Error('Unable to query organizations. Please try again.'), - ) + const query = jest.fn().mockRejectedValue(new Error('Unable to query organizations. Please try again.')) const affiliationLoader = loadAffiliationConnectionsByOrgId({ query, @@ -952,11 +847,9 @@ describe('given the load affiliations by org id function', () => { first: 5, } try { - await affiliationLoader({orgId: org._id, ...connectionArgs}) + await affiliationLoader({ orgId: org._id, ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error('Unable to query affiliation(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to query affiliation(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -986,11 +879,9 @@ describe('given the load affiliations by org id function', () => { first: 5, } try { - await affiliationLoader({orgId: org._id, ...connectionArgs}) + await affiliationLoader({ orgId: org._id, ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load affiliation(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load affiliation(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -1005,8 +896,8 @@ describe('given the load affiliations by org id function', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['fr'], messages: { @@ -1029,7 +920,7 @@ describe('given the load affiliations by org id function', () => { last: 1, } try { - await affiliationLoader({orgId: org._id, ...connectionArgs}) + await affiliationLoader({ orgId: org._id, ...connectionArgs }) } catch (err) { expect(err).toEqual( new Error( @@ -1092,9 +983,7 @@ describe('given the load affiliations by org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` sur la connexion \`Affiliation\` ne peut être inférieur à zéro.`, - ), + new Error(`\`first\` sur la connexion \`Affiliation\` ne peut être inférieur à zéro.`), ) } expect(consoleOutput).toEqual([ @@ -1121,11 +1010,7 @@ describe('given the load affiliations by org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` sur la connexion \`Affiliation\` ne peut être inférieur à zéro.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` sur la connexion \`Affiliation\` ne peut être inférieur à zéro.`)) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadAffiliationConnectionsByOrgId.`, @@ -1198,9 +1083,7 @@ describe('given the load affiliations by org id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const affiliationLoader = loadAffiliationConnectionsByOrgId({ query, userKey: user._key, @@ -1218,9 +1101,7 @@ describe('given the load affiliations by org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -1233,9 +1114,7 @@ describe('given the load affiliations by org id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const affiliationLoader = loadAffiliationConnectionsByOrgId({ query, userKey: user._key, @@ -1253,9 +1132,7 @@ describe('given the load affiliations by org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -1271,11 +1148,7 @@ describe('given the load affiliations by org id function', () => { describe('given a database error', () => { describe('while querying affiliations', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue( - new Error('Unable to query organizations. Please try again.'), - ) + const query = jest.fn().mockRejectedValue(new Error('Unable to query organizations. Please try again.')) const affiliationLoader = loadAffiliationConnectionsByOrgId({ query, @@ -1288,13 +1161,9 @@ describe('given the load affiliations by org id function', () => { first: 5, } try { - await affiliationLoader({orgId: org._id, ...connectionArgs}) + await affiliationLoader({ orgId: org._id, ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - `Impossible de demander l'affiliation (s). Veuillez réessayer.`, - ), - ) + expect(err).toEqual(new Error(`Impossible de demander l'affiliation (s). Veuillez réessayer.`)) } expect(consoleOutput).toEqual([ @@ -1324,13 +1193,9 @@ describe('given the load affiliations by org id function', () => { first: 5, } try { - await affiliationLoader({orgId: org._id, ...connectionArgs}) + await affiliationLoader({ orgId: org._id, ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - `Impossible de charger l'affiliation (s). Veuillez réessayer.`, - ), - ) + expect(err).toEqual(new Error(`Impossible de charger l'affiliation (s). Veuillez réessayer.`)) } expect(consoleOutput).toEqual([ diff --git a/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-user-id.test.js b/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-user-id.test.js index 9c967acba..2d6ca85ec 100644 --- a/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-user-id.test.js +++ b/api/src/affiliation/loaders/__tests__/load-affiliation-connections-by-user-id.test.js @@ -1,18 +1,19 @@ -import {stringify} from 'jest-matcher-utils' -import {ensure, dbNameFromFile} from 'arango-tools' -import {toGlobalId} from 'graphql-relay' -import {setupI18n} from '@lingui/core' +import { stringify } from 'jest-matcher-utils' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { toGlobalId } from 'graphql-relay' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' -import {cleanseInput} from '../../../validators' -import {loadAffiliationConnectionsByUserId, loadAffiliationByKey} from '..' +import { cleanseInput } from '../../../validators' +import { loadAffiliationConnectionsByUserId, loadAffiliationByKey } from '..' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the load affiliations by user id function', () => { - let query, drop, truncate, collections, user, orgOne, orgTwo, i18n + let query, drop, truncate, collections, db, user, orgOne, orgTwo, i18n const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -30,7 +31,7 @@ describe('given the load affiliations by user id function', () => { describe('given there are user affiliations to be returned', () => { let affOne, affTwo beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections, db } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -46,7 +47,6 @@ describe('given the load affiliations by user id function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -126,11 +126,8 @@ describe('given the load affiliations by user id function', () => { i18n, }) - const affLoader = loadAffiliationByKey({query}) - const expectedAffiliations = await affLoader.loadMany([ - affOne._key, - affTwo._key, - ]) + const affLoader = loadAffiliationByKey({ query }) + const expectedAffiliations = await affLoader.loadMany([affOne._key, affTwo._key]) expectedAffiliations[0].id = expectedAffiliations[0]._key expectedAffiliations[1].id = expectedAffiliations[1]._key @@ -147,10 +144,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[1]._key), node: { ...expectedAffiliations[1], }, @@ -160,14 +154,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), }, } @@ -203,10 +191,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -216,14 +201,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -258,10 +237,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -271,14 +247,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -313,10 +283,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[1]._key), node: { ...expectedAffiliations[1], }, @@ -326,14 +293,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[1]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[1]._key), }, } @@ -380,10 +341,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -393,14 +351,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -436,10 +388,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -449,14 +398,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -494,10 +437,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -507,14 +447,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -550,10 +484,7 @@ describe('given the load affiliations by user id function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + cursor: toGlobalId('affiliation', expectedAffiliations[0]._key), node: { ...expectedAffiliations[0], }, @@ -563,14 +494,8 @@ describe('given the load affiliations by user id function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), - endCursor: toGlobalId( - 'affiliation', - expectedAffiliations[0]._key, - ), + startCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), + endCursor: toGlobalId('affiliation', expectedAffiliations[0]._key), }, } @@ -580,16 +505,7 @@ describe('given the load affiliations by user id function', () => { }) }) describe('using orderBy field', () => { - let affOne, - affTwo, - affThree, - domainOne, - domainTwo, - domainThree, - orgOne, - orgTwo, - orgThree, - userOne + let affOne, affTwo, affThree, domainOne, domainTwo, domainThree, orgOne, orgTwo, orgThree, userOne beforeEach(async () => { await truncate() userOne = await collections.users.save({ @@ -597,18 +513,6 @@ describe('given the load affiliations by user id function', () => { }) orgOne = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, orgDetails: { en: { slug: 'slug-org-a', @@ -632,20 +536,15 @@ describe('given the load affiliations by user id function', () => { }, }, }) + const orgOneSummary = await collections.organizationSummaries.save({ + organization: orgOne._id, + web: { pass: 50, fail: 1000, total: 1050 }, + mail: { pass: 50, fail: 1000, total: 1050 }, + }) + await db.collection('organizations').update(orgOne._key, { latestSummaryId: orgOneSummary._id }) + orgTwo = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, orgDetails: { en: { slug: 'slug-org-b', @@ -669,20 +568,15 @@ describe('given the load affiliations by user id function', () => { }, }, }) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + web: { pass: 51, fail: 1001, total: 1052 }, + mail: { pass: 51, fail: 1001, total: 1052 }, + }) + await db.collection('organizations').update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) + orgThree = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, orgDetails: { en: { slug: 'slug-org-c', @@ -706,6 +600,12 @@ describe('given the load affiliations by user id function', () => { }, }, }) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + web: { pass: 52, fail: 1002, total: 1054 }, + mail: { pass: 52, fail: 1002, total: 1054 }, + }) + await db.collection('organizations').update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) affOne = await collections.affiliations.save({ _key: '1', _from: orgOne._id, @@ -1300,11 +1200,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1352,11 +1250,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_PROVINCE', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1402,11 +1298,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1454,11 +1348,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_CITY', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1504,11 +1396,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1556,11 +1446,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_VERIFIED', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1606,11 +1494,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1658,11 +1544,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SUMMARY_MAIL_PASS', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1708,11 +1592,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1760,11 +1642,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SUMMARY_MAIL_FAIL', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1810,11 +1690,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1862,11 +1740,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SUMMARY_MAIL_TOTAL', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1912,11 +1788,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -1964,11 +1838,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SUMMARY_WEB_PASS', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2014,11 +1886,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2066,11 +1936,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SUMMARY_WEB_FAIL', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2116,11 +1984,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2168,11 +2034,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SUMMARY_WEB_TOTAL', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2218,11 +2082,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2270,11 +2132,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_DOMAIN_COUNT', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2320,11 +2180,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2374,11 +2232,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_ACRONYM', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2424,11 +2280,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2476,11 +2330,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_NAME', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2526,11 +2378,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2578,11 +2428,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SLUG', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2628,11 +2476,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2680,11 +2526,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_ZONE', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2730,11 +2574,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2782,11 +2624,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_SECTOR', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2832,11 +2672,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2884,11 +2722,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_COUNTRY', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2934,11 +2770,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -2986,11 +2820,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_PROVINCE', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -3036,11 +2868,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -3088,11 +2918,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_CITY', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -3138,11 +2966,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -3190,11 +3016,9 @@ describe('given the load affiliations by user id function', () => { describe('ordering by ORG_VERIFIED', () => { describe('direction is set to ASC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -3240,11 +3064,9 @@ describe('given the load affiliations by user id function', () => { }) describe('direction is set to DESC', () => { it('returns affiliation', async () => { - const affiliationLoader = loadAffiliationByKey({query}) + const affiliationLoader = loadAffiliationByKey({ query }) - const expectedAffiliation = await affiliationLoader.load( - affTwo._key, - ) + const expectedAffiliation = await affiliationLoader.load(affTwo._key) const connectionLoader = loadAffiliationConnectionsByUserId({ query, @@ -4019,8 +3841,8 @@ describe('given the load affiliations by user id function', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en'], messages: { @@ -4050,9 +3872,7 @@ describe('given the load affiliations by user id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.', - ), + new Error('Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.'), ) } @@ -4111,11 +3931,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `Affiliation` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `Affiliation` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadAffiliationConnectionsByUserId.`, @@ -4142,11 +3958,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `Affiliation` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `Affiliation` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadAffiliationConnectionsByUserId.`, @@ -4221,9 +4033,7 @@ describe('given the load affiliations by user id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadAffiliationConnectionsByUserId({ query, language: 'en', @@ -4241,11 +4051,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -4257,9 +4063,7 @@ describe('given the load affiliations by user id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadAffiliationConnectionsByUserId({ query, language: 'en', @@ -4277,11 +4081,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -4296,11 +4096,7 @@ describe('given the load affiliations by user id function', () => { describe('given a database error', () => { describe('while querying domains', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue( - new Error('Unable to query organizations. Please try again.'), - ) + const query = jest.fn().mockRejectedValue(new Error('Unable to query organizations. Please try again.')) const affiliationLoader = loadAffiliationConnectionsByUserId({ query, @@ -4319,9 +4115,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to query affiliation(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to query affiliation(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -4357,9 +4151,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load affiliation(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load affiliation(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -4374,8 +4166,8 @@ describe('given the load affiliations by user id function', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -4467,11 +4259,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` sur la connexion `Affiliation` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`first` sur la connexion `Affiliation` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadAffiliationConnectionsByUserId.`, @@ -4498,11 +4286,7 @@ describe('given the load affiliations by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` sur la connexion `Affiliation` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`last` sur la connexion `Affiliation` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadAffiliationConnectionsByUserId.`, @@ -4577,9 +4361,7 @@ describe('given the load affiliations by user id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadAffiliationConnectionsByUserId({ query, language: 'en', @@ -4598,9 +4380,7 @@ describe('given the load affiliations by user id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -4613,9 +4393,7 @@ describe('given the load affiliations by user id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadAffiliationConnectionsByUserId({ query, language: 'en', @@ -4634,9 +4412,7 @@ describe('given the load affiliations by user id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -4652,11 +4428,7 @@ describe('given the load affiliations by user id function', () => { describe('given a database error', () => { describe('while querying domains', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue( - new Error('Unable to query organizations. Please try again.'), - ) + const query = jest.fn().mockRejectedValue(new Error('Unable to query organizations. Please try again.')) const affiliationLoader = loadAffiliationConnectionsByUserId({ query, @@ -4670,13 +4442,9 @@ describe('given the load affiliations by user id function', () => { first: 5, } try { - await affiliationLoader({userId: user._id, ...connectionArgs}) + await affiliationLoader({ userId: user._id, ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de demander l'affiliation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de demander l'affiliation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -4707,13 +4475,9 @@ describe('given the load affiliations by user id function', () => { first: 5, } try { - await affiliationLoader({userId: user._id, ...connectionArgs}) + await affiliationLoader({ userId: user._id, ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'affiliation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'affiliation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/affiliation/loaders/load-affiliation-connections-by-org-id.js b/api/src/affiliation/loaders/load-affiliation-connections-by-org-id.js index fa32893b2..578943de5 100644 --- a/api/src/affiliation/loaders/load-affiliation-connections-by-org-id.js +++ b/api/src/affiliation/loaders/load-affiliation-connections-by-org-id.js @@ -20,9 +20,15 @@ export const loadAffiliationConnectionsByOrgId = let affiliationField, documentField /* istanbul ignore else */ - if (orderBy.field === 'user-username') { + if (orderBy.field === 'username') { affiliationField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).userName` documentField = aql`DOCUMENT(users, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._to).key).userName` + } else if (orderBy.field === 'display_name') { + affiliationField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).displayName` + documentField = aql`DOCUMENT(users, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._to).key).displayName` + } else if (orderBy.field === 'permission') { + affiliationField = aql`rolePriority[affiliation.permission]` + documentField = aql`rolePriority[DOCUMENT(affiliations, ${afterId}).permission]` } afterTemplate = aql` @@ -48,9 +54,15 @@ export const loadAffiliationConnectionsByOrgId = let affiliationField, documentField /* istanbul ignore else */ - if (orderBy.field === 'user-username') { + if (orderBy.field === 'username') { affiliationField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).userName` documentField = aql`DOCUMENT(users, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._to).key).userName` + } else if (orderBy.field === 'display_name') { + affiliationField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).displayName` + documentField = aql`DOCUMENT(users, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._to).key).displayName` + } else if (orderBy.field === 'permission') { + affiliationField = aql`rolePriority[affiliation.permission]` + documentField = aql`rolePriority[DOCUMENT(affiliations, ${beforeId}).permission]` } beforeTemplate = aql` @@ -124,10 +136,18 @@ export const loadAffiliationConnectionsByOrgId = let affField, hasNextPageDocument, hasPreviousPageDocument /* istanbul ignore else */ - if (orderBy.field === 'user-username') { + if (orderBy.field === 'username') { affField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).userName` hasNextPageDocument = aql`DOCUMENT(users, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._to).key).userName` hasPreviousPageDocument = aql`DOCUMENT(users, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._to).key).userName` + } else if (orderBy.field === 'display_name') { + affField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).displayName` + hasNextPageDocument = aql`DOCUMENT(users, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._to).key).displayName` + hasPreviousPageDocument = aql`DOCUMENT(users, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._to).key).displayName` + } else if (orderBy.field === 'permission') { + affField = aql`rolePriority[affiliation.permission]` + hasNextPageDocument = aql`rolePriority[LAST(retrievedAffiliations).permission]` + hasPreviousPageDocument = aql`FIRST(retrievedAffiliations).permission` } hasNextPageFilter = aql` @@ -146,8 +166,12 @@ export const loadAffiliationConnectionsByOrgId = let sortByField = aql`` if (typeof orderBy !== 'undefined') { /* istanbul ignore else */ - if (orderBy.field === 'user-username') { + if (orderBy.field === 'username') { sortByField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).userName ${orderBy.direction},` + } else if (orderBy.field === 'display_name') { + sortByField = aql`DOCUMENT(users, PARSE_IDENTIFIER(affiliation._to).key).displayName ${orderBy.direction},` + } else if (orderBy.field === 'permission') { + sortByField = aql`rolePriority[affiliation.permission] ${orderBy.direction},` } } @@ -164,15 +188,27 @@ export const loadAffiliationConnectionsByOrgId = search = cleanseInput(search) userSearchQuery = aql` LET tokenArr = TOKENS(${search}, "text_en") - LET userIds = UNIQUE( - FOR token IN tokenArr + LET searchedDisplayNamesCount = FLATTEN( + FOR tokenItem in tokenArr + LET token = LOWER(tokenItem) FOR user IN userSearch SEARCH ANALYZER( user.displayName LIKE CONCAT("%", token, "%") - OR user.userName LIKE CONCAT("%", token, "%") , "text_en") - RETURN user._id + COLLECT currentUserId = user._id WITH COUNT INTO count + RETURN { + 'user': currentUserId, + 'count': count + } ) + LET searchedDisplayNames = searchedDisplayNamesCount[* FILTER CURRENT.count == LENGTH(tokenArr)].user + LET searchedUserNames = ( + FOR user IN users + FILTER LOWER(user.userName) LIKE CONCAT("%", LOWER(${search}), "%") + RETURN user._id + ) + LET userIds = UNIQUE(APPEND(searchedDisplayNames, searchedUserNames)) + ` userIdFilter = aql`FILTER e._to IN userIds` } @@ -196,6 +232,14 @@ export const loadAffiliationConnectionsByOrgId = RETURN e._key ) + LET rolePriority = { + "pending": 0, + "owner": 1, + "super_admin": 2, + "admin": 3, + "user": 4 + } + LET retrievedAffiliations = ( FOR affiliation IN affiliations FILTER affiliation._key IN affiliationKeys diff --git a/api/src/affiliation/loaders/load-affiliation-connections-by-user-id.js b/api/src/affiliation/loaders/load-affiliation-connections-by-user-id.js index 7e14151d7..d84fd6898 100644 --- a/api/src/affiliation/loaders/load-affiliation-connections-by-user-id.js +++ b/api/src/affiliation/loaders/load-affiliation-connections-by-user-id.js @@ -48,23 +48,23 @@ export const loadAffiliationConnectionsByUserId = affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).verified` documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).verified` } else if (orderBy.field === 'org-summary-mail-pass') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.pass` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).summaries.mail.pass` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.pass` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).latestSummaryId).mail.pass` } else if (orderBy.field === 'org-summary-mail-fail') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.fail` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).summaries.mail.fail` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.fail` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).latestSummaryId).mail.fail` } else if (orderBy.field === 'org-summary-mail-total') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.total` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).summaries.mail.total` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.total` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).latestSummaryId).mail.total` } else if (orderBy.field === 'org-summary-web-pass') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.pass` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).summaries.web.pass` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.pass` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).latestSummaryId).web.pass` } else if (orderBy.field === 'org-summary-web-fail') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.fail` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).summaries.web.fail` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.fail` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).latestSummaryId).web.fail` } else if (orderBy.field === 'org-summary-web-total') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.total` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).summaries.web.total` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.total` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key).latestSummaryId).web.total` } else if (orderBy.field === 'org-domain-count') { affiliationField = aql`COUNT(FOR v, e IN 1..1 ANY DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key)._id claims RETURN e._to)` documentField = aql`COUNT(FOR v, e IN 1..1 ANY DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${afterId})._from).key)._id claims RETURN e._to)` @@ -121,23 +121,23 @@ export const loadAffiliationConnectionsByUserId = affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).verified` documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).verified` } else if (orderBy.field === 'org-summary-mail-pass') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.pass` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).summaries.mail.pass` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.pass` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).latestSummaryId).mail.pass` } else if (orderBy.field === 'org-summary-mail-fail') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.fail` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).summaries.mail.fail` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.fail` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).latestSummaryId).mail.fail` } else if (orderBy.field === 'org-summary-mail-total') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.total` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).summaries.mail.total` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.total` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).latestSummaryId).mail.total` } else if (orderBy.field === 'org-summary-web-pass') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.pass` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).summaries.web.pass` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.pass` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).latestSummaryId).web.pass` } else if (orderBy.field === 'org-summary-web-fail') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.fail` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).summaries.web.fail` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.fail` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).latestSummaryId).web.fail` } else if (orderBy.field === 'org-summary-web-total') { - affiliationField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.total` - documentField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).summaries.web.total` + affiliationField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.total` + documentField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key).latestSummaryId).web.total` } else if (orderBy.field === 'org-domain-count') { affiliationField = aql`COUNT(FOR v, e IN 1..1 ANY DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key)._id claims RETURN e._to)` documentField = aql`COUNT(FOR v, e IN 1..1 ANY DOCUMENT(organizations, PARSE_IDENTIFIER(DOCUMENT(affiliations, ${beforeId})._from).key)._id claims RETURN e._to)` @@ -261,29 +261,29 @@ export const loadAffiliationConnectionsByUserId = hasNextPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).verified` hasPreviousPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).verified` } else if (orderBy.field === 'org-summary-mail-pass') { - affField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.pass` - hasNextPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).summaries.mail.pass` - hasPreviousPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).summaries.mail.pass` + affField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.pass` + hasNextPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).latestSummaryId).mail.pass` + hasPreviousPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).latestSummaryId).mail.pass` } else if (orderBy.field === 'org-summary-mail-fail') { - affField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.fail` - hasNextPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).summaries.mail.fail` - hasPreviousPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).summaries.mail.fail` + affField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.fail` + hasNextPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).latestSummaryId).mail.fail` + hasPreviousPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).latestSummaryId).mail.fail` } else if (orderBy.field === 'org-summary-mail-total') { - affField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.total` - hasNextPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).summaries.mail.total` - hasPreviousPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).summaries.mail.total` + affField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.total` + hasNextPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).latestSummaryId).mail.total` + hasPreviousPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).latestSummaryId).mail.total` } else if (orderBy.field === 'org-summary-web-pass') { - affField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.pass` - hasNextPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).summaries.web.pass` - hasPreviousPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).summaries.web.pass` + affField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.pass` + hasNextPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).latestSummaryId).web.pass` + hasPreviousPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).latestSummaryId).web.pass` } else if (orderBy.field === 'org-summary-web-fail') { - affField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.fail` - hasNextPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).summaries.web.fail` - hasPreviousPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).summaries.web.fail` + affField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.fail` + hasNextPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).latestSummaryId).web.fail` + hasPreviousPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).latestSummaryId).web.fail` } else if (orderBy.field === 'org-summary-web-total') { - affField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.total` - hasNextPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).summaries.web.total` - hasPreviousPageDocument = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).summaries.web.total` + affField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.total` + hasNextPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key).latestSummaryId).web.total` + hasPreviousPageDocument = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(FIRST(retrievedAffiliations)._from).key).latestSummaryId).web.total` } else if (orderBy.field === 'org-domain-count') { affField = aql`COUNT(FOR v, e IN 1..1 ANY DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key)._id claims RETURN e._to)` hasNextPageDocument = aql`COUNT(FOR v, e IN 1..1 ANY DOCUMENT(organizations, PARSE_IDENTIFIER(LAST(retrievedAffiliations)._from).key)._id claims RETURN e._to)` @@ -325,17 +325,17 @@ export const loadAffiliationConnectionsByUserId = } else if (orderBy.field === 'org-verified') { sortByField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).verified ${orderBy.direction},` } else if (orderBy.field === 'org-summary-mail-pass') { - sortByField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.pass ${orderBy.direction},` } else if (orderBy.field === 'org-summary-mail-fail') { - sortByField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.fail ${orderBy.direction},` } else if (orderBy.field === 'org-summary-mail-total') { - sortByField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.mail.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).mail.total ${orderBy.direction},` } else if (orderBy.field === 'org-summary-web-pass') { - sortByField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.pass ${orderBy.direction},` } else if (orderBy.field === 'org-summary-web-fail') { - sortByField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.fail ${orderBy.direction},` } else if (orderBy.field === 'org-summary-web-total') { - sortByField = aql`DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).summaries.web.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key).latestSummaryId).web.total ${orderBy.direction},` } else if (orderBy.field === 'org-domain-count') { sortByField = aql`COUNT(FOR v, e IN 1..1 ANY DOCUMENT(organizations, PARSE_IDENTIFIER(affiliation._from).key)._id claims RETURN e._to) ${orderBy.direction},` } diff --git a/api/src/affiliation/mutations/__tests__/invite-user-to-org.test.js b/api/src/affiliation/mutations/__tests__/invite-user-to-org.test.js index 83e8adba4..ed82606ae 100644 --- a/api/src/affiliation/mutations/__tests__/invite-user-to-org.test.js +++ b/api/src/affiliation/mutations/__tests__/invite-user-to-org.test.js @@ -1,6 +1,7 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' -import { graphql, GraphQLSchema } from 'graphql' +import { graphql as executeGraphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' @@ -9,13 +10,40 @@ import { checkPermission, userRequired, verifiedRequired, tfaRequired } from '.. import { createMutationSchema } from '../../../mutation' import { createQuerySchema } from '../../../query' import { cleanseInput } from '../../../validators' -import { loadOrgByKey } from '../../../organization/loaders' +import { loadOrgByKey, loadOrganizationNamesById } from '../../../organization/loaders' import { loadUserByKey, loadUserByUserName } from '../../../user/loaders' +import { AffiliationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url, SIGN_IN_KEY } = process.env +const withAffiliationDataSource = (contextValue = {}) => { + const dataSources = contextValue.dataSources || {} + if (dataSources.affiliation && dataSources.auditLogs) return contextValue + + return { + ...contextValue, + dataSources: { + ...dataSources, + affiliation: + dataSources.affiliation || + new AffiliationDataSource({ + query: contextValue.query, + transaction: contextValue.transaction, + collections: contextValue.collections, + userKey: contextValue.userKey, + i18n: contextValue.i18n, + language: contextValue.request?.language, + cleanseInput: contextValue.validators?.cleanseInput, + }), + auditLogs: dataSources.auditLogs || { logActivity: jest.fn().mockResolvedValue(undefined) }, + }, + } +} + +const graphql = (args) => executeGraphql({ ...args, contextValue: withAffiliationDataSource(args.contextValue) }) + describe('invite user to org', () => { let query, drop, truncate, schema, collections, transaction, i18n, tokenize, user, org, userToInvite @@ -52,6 +80,18 @@ describe('invite user to org', () => { schema: dbschema, })) tokenize = jest.fn().mockReturnValue('token') + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) }) beforeEach(async () => { user = await collections.users.save({ @@ -66,77 +106,62 @@ describe('invite user to org', () => { afterAll(async () => { await drop() }) - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - let org - beforeEach(async () => { - org = await ( - await collections.organizations.save( - { - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, + beforeAll(() => {}) + let org + beforeEach(async () => { + org = await ( + await collections.organizations.save( + { + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', }, }, - { returnNew: true }, - ) - ).new - }) - describe('users role is super admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'super_admin', - }) + }, + { returnNew: true }, + ) + ).new + }) + describe('users role is super admin', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'super_admin', }) - describe('inviting an existing account', () => { - describe('requested role is super_admin', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'english', - }) + }) + describe('inviting an existing account', () => { + describe('requested role is super_admin', () => { + let secondaryUser + beforeEach(async () => { + secondaryUser = await collections.users.save({ + displayName: 'Test Account', + userName: 'test@email.gc.ca', }) - it('returns status message', async () => { - const sendOrgInviteEmail = jest.fn() + }) + it('returns status message', async () => { + const sendOrgInviteEmail = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -157,82 +182,81 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteEmail: sendOrgInviteEmail }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully invited user to organization, and sent notification email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully invited user to organization, and sent notification email.', }, }, - } + }, + } - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'english', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - describe('requested role is admin', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteEmail).toHaveBeenCalledWith({ + user: { + _type: 'user', displayName: 'Test Account', + id: secondaryUser._key, userName: 'test@email.gc.ca', - preferredLang: 'english', - }) + ...secondaryUser, + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', + }) + }) + }) + describe('requested role is admin', () => { + let secondaryUser + beforeEach(async () => { + secondaryUser = await collections.users.save({ + displayName: 'Test Account', + userName: 'test@email.gc.ca', }) - it('returns status message', async () => { - const sendOrgInviteEmail = jest.fn() + }) + it('returns status message', async () => { + const sendOrgInviteEmail = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -253,82 +277,81 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteEmail: sendOrgInviteEmail }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully invited user to organization, and sent notification email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully invited user to organization, and sent notification email.', }, }, - } + }, + } - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'english', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - describe('requested role is user', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteEmail).toHaveBeenCalledWith({ + user: { + _type: 'user', displayName: 'Test Account', + id: secondaryUser._key, userName: 'test@email.gc.ca', - preferredLang: 'english', - }) + ...secondaryUser, + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', }) - it('returns status message', async () => { - const sendOrgInviteEmail = jest.fn() + }) + }) + describe('requested role is user', () => { + let secondaryUser + beforeEach(async () => { + secondaryUser = await collections.users.save({ + displayName: 'Test Account', + userName: 'test@email.gc.ca', + }) + }) + it('returns status message', async () => { + const sendOrgInviteEmail = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -349,75 +372,75 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteEmail: sendOrgInviteEmail }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully invited user to organization, and sent notification email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully invited user to organization, and sent notification email.', }, }, - } + }, + } - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'english', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteEmail).toHaveBeenCalledWith({ + user: { + _type: 'user', + displayName: 'Test Account', + id: secondaryUser._key, + userName: 'test@email.gc.ca', + ...secondaryUser, + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', }) }) }) - describe('inviting a non-existing account', () => { - describe('requested role is super_admin', () => { - it('returns status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() - const response = await graphql({ - schema, - source: ` + }) + describe('inviting a non-existing account', () => { + describe('requested role is super_admin', () => { + it('returns status message', async () => { + const sendOrgInviteCreateAccount = jest.fn() + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -438,80 +461,81 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteCreateAccount }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully sent invitation to service, and organization email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully sent invitation to service, and organization email.', }, }, - } + }, + } - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'super_admin', - }, - }) - const createAccountLink = `https://host/create-user/${token}` - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { - userName: 'test@email.gc.ca', - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) + const token = tokenize({ + parameters: { + userName: 'test@email.gc.ca', + orgId: org._id, + requestedRole: 'super_admin', + }, + }) + const createAccountLink = `https://host/create-user/${token}` + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ + user: { + userName: 'test@email.gc.ca', + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', + createAccountLink, }) }) - describe('requested role is admin', () => { - it('returns status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() + }) + describe('requested role is admin', () => { + it('returns status message', async () => { + const sendOrgInviteCreateAccount = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -532,81 +556,82 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteCreateAccount }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully sent invitation to service, and organization email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully sent invitation to service, and organization email.', }, }, - } + }, + } - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'admin', - }, - }) - const createAccountLink = `https://host/create-user/${token}` + const token = tokenize({ + parameters: { + userName: 'test@email.gc.ca', + orgId: org._id, + requestedRole: 'admin', + }, + }) + const createAccountLink = `https://host/create-user/${token}` - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { - userName: 'test@email.gc.ca', - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ + user: { + userName: 'test@email.gc.ca', + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', + createAccountLink, }) }) - describe('requested role is user', () => { - it('returns status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() + }) + describe('requested role is user', () => { + it('returns status message', async () => { + const sendOrgInviteCreateAccount = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -627,100 +652,100 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + protocol: 'https', + get: (text) => text, }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully sent invitation to service, and organization email.', - }, - }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, + loadUserByKey: loadUserByKey({ query }), + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), }, - } - - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'user', + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) - const createAccountLink = `https://host/create-user/${token}` + notify: { sendOrgInviteCreateAccount }, + validators: { cleanseInput }, + }, + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { - userName: 'test@email.gc.ca', + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully sent invitation to service, and organization email.', + }, }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) + }, + } + + const token = tokenize({ + parameters: { + userName: 'test@email.gc.ca', + orgId: org._id, + requestedRole: 'user', + }, + }) + const createAccountLink = `https://host/create-user/${token}` + + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ + user: { + userName: 'test@email.gc.ca', + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', + createAccountLink, }) }) }) }) - describe('users role is admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'admin', - }) + }) + describe('users role is admin', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'admin', }) - describe('inviting an existing account', () => { - describe('requested role is admin', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'english', - }) + }) + describe('inviting an existing account', () => { + describe('requested role is admin', () => { + let secondaryUser + beforeEach(async () => { + secondaryUser = await collections.users.save({ + displayName: 'Test Account', + userName: 'test@email.gc.ca', }) - it('returns status message', async () => { - const sendOrgInviteEmail = jest.fn() + }) + it('returns status message', async () => { + const sendOrgInviteEmail = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -741,82 +766,81 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteEmail: sendOrgInviteEmail }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully invited user to organization, and sent notification email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully invited user to organization, and sent notification email.', }, }, - } + }, + } - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'english', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - describe('requested role is user', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteEmail).toHaveBeenCalledWith({ + user: { + _type: 'user', displayName: 'Test Account', + id: secondaryUser._key, userName: 'test@email.gc.ca', - preferredLang: 'english', - }) + ...secondaryUser, + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', }) - it('returns status message', async () => { - const sendOrgInviteEmail = jest.fn() + }) + }) + describe('requested role is user', () => { + let secondaryUser + beforeEach(async () => { + secondaryUser = await collections.users.save({ + displayName: 'Test Account', + userName: 'test@email.gc.ca', + }) + }) + it('returns status message', async () => { + const sendOrgInviteEmail = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -837,76 +861,76 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteEmail: sendOrgInviteEmail }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully invited user to organization, and sent notification email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully invited user to organization, and sent notification email.', }, }, - } + }, + } - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'english', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteEmail).toHaveBeenCalledWith({ + user: { + _type: 'user', + displayName: 'Test Account', + id: secondaryUser._key, + userName: 'test@email.gc.ca', + ...secondaryUser, + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', }) }) }) - describe('inviting a non-existing account', () => { - describe('requested role is admin', () => { - it('returns status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() + }) + describe('inviting a non-existing account', () => { + describe('requested role is admin', () => { + it('returns status message', async () => { + const sendOrgInviteCreateAccount = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -927,81 +951,82 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteCreateAccount }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully sent invitation to service, and organization email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully sent invitation to service, and organization email.', }, }, - } + }, + } - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'admin', - }, - }) - const createAccountLink = `https://host/create-user/${token}` + const token = tokenize({ + parameters: { + userName: 'test@email.gc.ca', + orgId: org._id, + requestedRole: 'admin', + }, + }) + const createAccountLink = `https://host/create-user/${token}` - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { - userName: 'test@email.gc.ca', - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ + user: { + userName: 'test@email.gc.ca', + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', + createAccountLink, }) }) - describe('requested role is user', () => { - it('returns status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() + }) + describe('requested role is user', () => { + it('returns status message', async () => { + const sendOrgInviteCreateAccount = jest.fn() - const response = await graphql({ - schema, - source: ` + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { @@ -1022,1152 +1047,139 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, + }), + tokenize, + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, + }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, - }) + notify: { sendOrgInviteCreateAccount }, + validators: { cleanseInput }, + }, + }) - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: 'Successfully sent invitation to service, and organization email.', - }, + const expectedResponse = { + data: { + inviteUserToOrg: { + result: { + status: 'Successfully sent invitation to service, and organization email.', }, }, - } + }, + } - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'user', - }, - }) - const createAccountLink = `https://host/create-user/${token}` + const token = tokenize({ + parameters: { + userName: 'test@email.gc.ca', + orgId: org._id, + requestedRole: 'user', + }, + }) + const createAccountLink = `https://host/create-user/${token}` - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { - userName: 'test@email.gc.ca', - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: treasury-board-secretariat.`, + ]) + expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ + user: { + userName: 'test@email.gc.ca', + }, + orgNameEN: 'Treasury Board of Canada Secretariat', + orgNameFR: 'Secrétariat du Conseil Trésor du Canada', + createAccountLink, }) }) }) }) }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, + }) + describe('given an unsuccessful invitation', () => { + beforeAll(async () => { + ;({ query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) + tokenize = jest.fn().mockReturnValue('token') + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + beforeEach(async () => { + user = ( + await collections.users.save( + { + userName: 'test.account@istio.actually.exists', + emailValidated: true, + tfaSendMethod: 'email', }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, + { returnNew: true }, + ) + ).new + userToInvite = ( + await collections.users.save( + { + userName: 'usertoinvite@istio.actually.exists', + emailValidated: true, + tfaSendMethod: 'email', }, - }) - }) - let org - beforeEach(async () => { - org = await ( - await collections.organizations.save( - { - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - { returnNew: true }, - ) - ).new - }) - describe('users role is super admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'super_admin', - }) - }) - describe('inviting an existing account', () => { - describe('requested role is super_admin', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'french', - }) - }) - it('returns a status message', async () => { - const sendOrgInviteEmail = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: SUPER_ADMIN - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: - "L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé.", - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'french', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - describe('requested role is admin', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'french', - }) - }) - it('returns a status message', async () => { - const sendOrgInviteEmail = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: ADMIN - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: - "L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé.", - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'french', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - describe('requested role is user', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'french', - }) - }) - it('returns a status message', async () => { - const sendOrgInviteEmail = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: - "L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé.", - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'french', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - }) - describe('inviting a non-existing account', () => { - describe('requested role is super_admin', () => { - it('returns a status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: SUPER_ADMIN - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: "Envoi réussi de l'invitation au service, et de l'email de l'organisation.", - }, - }, - }, - } - - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'super_admin', - }, - }) - const createAccountLink = `https://host/create-user/${token}` - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { userName: 'test@email.gc.ca' }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) - }) - }) - describe('requested role is admin', () => { - it('returns a status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: ADMIN - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: "Envoi réussi de l'invitation au service, et de l'email de l'organisation.", - }, - }, - }, - } - - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'admin', - }, - }) - const createAccountLink = `https://host/create-user/${token}` - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { userName: 'test@email.gc.ca' }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) - }) - }) - describe('requested role is user', () => { - it('returns a status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: "Envoi réussi de l'invitation au service, et de l'email de l'organisation.", - }, - }, - }, - } - - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'user', - }, - }) - const createAccountLink = `https://host/create-user/${token}` - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { userName: 'test@email.gc.ca' }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) - }) - }) - }) - }) - describe('users role is admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'admin', - }) - }) - describe('inviting an existing account', () => { - describe('requested role is admin', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'french', - }) - }) - it('returns a status message', async () => { - const sendOrgInviteEmail = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: ADMIN - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: - "L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé.", - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'french', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - describe('requested role is user', () => { - let secondaryUser - beforeEach(async () => { - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'french', - }) - }) - it('returns a status message', async () => { - const sendOrgInviteEmail = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteEmail: sendOrgInviteEmail }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: - "L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé.", - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: ${secondaryUser._key} to the org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteEmail).toHaveBeenCalledWith({ - user: { - _type: 'user', - displayName: 'Test Account', - id: secondaryUser._key, - preferredLang: 'french', - userName: 'test@email.gc.ca', - ...secondaryUser, - }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - }) - }) - }) - }) - describe('inviting a non-existing account', () => { - describe('requested role is admin', () => { - it('returns a status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: ADMIN - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: "Envoi réussi de l'invitation au service, et de l'email de l'organisation.", - }, - }, - }, - } - - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'admin', - }, - }) - const createAccountLink = `https://host/create-user/${token}` - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { userName: 'test@email.gc.ca' }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) - }) - }) - describe('requested role is user', () => { - it('returns a status message', async () => { - const sendOrgInviteCreateAccount = jest.fn() - - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount }, - validators: { cleanseInput }, - }, - }) - - const expectedResponse = { - data: { - inviteUserToOrg: { - result: { - status: "Envoi réussi de l'invitation au service, et de l'email de l'organisation.", - }, - }, - }, - } - - const token = tokenize({ - parameters: { - userName: 'test@email.gc.ca', - orgId: org._id, - requestedRole: 'user', - }, - }) - const createAccountLink = `https://host/create-user/${token}` - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully invited user: test@email.gc.ca to the service, and org: secretariat-conseil-tresor.`, - ]) - expect(sendOrgInviteCreateAccount).toHaveBeenCalledWith({ - user: { userName: 'test@email.gc.ca' }, - orgNameEN: 'Treasury Board of Canada Secretariat', - orgNameFR: 'Secrétariat du Conseil Trésor du Canada', - createAccountLink, - }) - }) - }) - }) - }) - }) - }) - describe('given an unsuccessful invitation', () => { - beforeAll(async () => { - ;({ query, drop, truncate, collections, transaction } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - tokenize = jest.fn().mockReturnValue('token') - }) - beforeEach(async () => { - user = ( - await collections.users.save( - { - userName: 'test.account@istio.actually.exists', - emailValidated: true, - tfaSendMethod: 'email', - }, - { returnNew: true }, - ) - ).new - userToInvite = ( - await collections.users.save( - { - userName: 'usertoinvite@istio.actually.exists', - emailValidated: true, - tfaSendMethod: 'email', - }, - { returnNew: true }, - ) - ).new - org = ( - await collections.organizations.save( - { - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', + { returnNew: true }, + ) + ).new + org = ( + await collections.organizations.save( + { + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', }, fr: { slug: 'secretariat-conseil-tresor', @@ -2182,271 +1194,27 @@ describe('invite user to org', () => { }, }, { returnNew: true }, - ) - ).new - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('user attempts to invite themselves', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "${user.userName}" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query, i18n }), - }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, - }, - }) - - const error = { - data: { - inviteUserToOrg: { - result: { - code: 400, - description: "Impossible de s'inviter à un org.", - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([`User: 123 attempted to invite themselves to ${org._key}.`]) - }) - }) - describe('user attempts to invite to an org that does not exist', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: USER - orgId: "${toGlobalId('organizations', 1)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, - }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, - }, - }) - - const error = { - data: { - inviteUserToOrg: { - result: { - code: 400, - description: "Impossible d'inviter un utilisateur à une organisation inconnue.", - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to 1 however there is no org associated with that id.`, - ]) - }) - }) - describe('user with undefined permission attempts to invite a user', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "test@email.gc.ca" - requestedRole: USER - orgId: "${toGlobalId('organizations', 123)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue(undefined), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _key: 123 }), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, - }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, - }, - }) - - const error = { - data: { - inviteUserToOrg: { - result: { - code: 403, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant les invitations d'utilisateurs.", - }, - }, - }, - } + ) + ).new + }) + afterEach(async () => { + await truncate() + }) + afterAll(async () => { + await drop() + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: user but does not have permission to do so.`, - ]) - }) - }) - describe('user with user level permission attempts to invite a user', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + describe('user attempts to invite themselves', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { - userName: "test@email.gc.ca" + userName: "test.account@istio.actually.exists" requestedRole: USER - orgId: "${toGlobalId('organizations', 123)}" + orgId: "${toGlobalId('organizations', 1)}" } ) { result { @@ -2461,72 +1229,72 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, + rootValue: null, + contextValue: { + i18n, + request: { + language: 'fr', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('admin'), + tokenize, + userRequired: jest.fn().mockReturnValue({ + userName: 'test.account@istio.actually.exists', + }), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadOrgByKey: { + load: jest.fn(), }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('user'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), + loadUserByKey: { + load: jest.fn(), }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _key: 123 }), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, + loadUserByUserName: { + load: jest.fn(), + }, + loadOrganizationNamesById: { + load: jest.fn(), }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, }, - }) + notify: { sendOrgInviteCreateAccount: jest.fn() }, + validators: { cleanseInput }, + }, + }) - const error = { - data: { - inviteUserToOrg: { - result: { - code: 403, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant les invitations d'utilisateurs.", - }, + const error = { + data: { + inviteUserToOrg: { + result: { + code: 400, + description: 'Unable to invite yourself to an org.', }, }, - } + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: user but does not have permission to do so.`, - ]) - }) + expect(response).toEqual(error) + expect(consoleOutput).toEqual([`User: 123 attempted to invite themselves to 1.`]) }) - describe('user with admin level permission attempts to invite a user to super_admin permission', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + }) + describe('user attempts to invite to an org that does not exist', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { userName: "test@email.gc.ca" - requestedRole: SUPER_ADMIN - orgId: "${toGlobalId('organizations', 123)}" + requestedRole: USER + orgId: "${toGlobalId('organizations', 1)}" } ) { result { @@ -2541,244 +1309,74 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, + rootValue: null, + contextValue: { + i18n, + request: { + language: 'fr', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('admin'), + tokenize, + userRequired: jest.fn().mockReturnValue({ + userName: 'test.account@exists.ca', + }), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadOrgByKey: { + load: jest.fn().mockReturnValue(undefined), }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), + loadUserByKey: { + load: jest.fn(), }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _key: 123 }), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, + loadUserByUserName: { + load: jest.fn(), }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, - }, - }) - - const error = { - data: { - inviteUserToOrg: { - result: { - code: 403, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant les invitations d'utilisateurs.", - }, + loadOrganizationNamesById: { + load: jest.fn(), }, }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: super_admin but does not have permission to do so.`, - ]) - }) - }) - describe('transaction error occurs', () => { - describe('when creating affiliation', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "${userToInvite.userName}" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue('trx step err'), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query, i18n }), - }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, - }, - }) - - const error = { - data: { - inviteUserToOrg: { - result: { - code: 500, - description: "Impossible d'inviter un utilisateur. Veuillez réessayer.", - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred while user: 123 attempted to invite user: ${userToInvite._key} to org: ${org.orgDetails.fr.slug}, error: trx step err`, - ]) - }) + notify: { sendOrgInviteCreateAccount: jest.fn() }, + validators: { cleanseInput }, + }, }) - describe('when committing transaction', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "${userToInvite.userName}" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn(), - commit: jest.fn().mockRejectedValue('trx commit err'), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { - sendOrgInviteCreateAccount: jest.fn(), - sendOrgInviteEmail: jest.fn(), - }, - validators: { cleanseInput }, - }, - }) - const error = { - data: { - inviteUserToOrg: { - result: { - code: 500, - description: "Impossible d'inviter un utilisateur. Veuillez réessayer.", - }, - }, + const error = { + data: { + inviteUserToOrg: { + result: { + code: 400, + description: 'Unable to invite user to unknown organization.', }, - } + }, + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction commit error occurred while user: 123 attempted to invite user: ${userToInvite._key} to org: secretariat-conseil-tresor, error: trx commit err`, - ]) - }) - }) + expect(response).toEqual(error) + expect(consoleOutput).toEqual([ + `User: 123 attempted to invite user: test@email.gc.ca to 1 however there is no org associated with that id.`, + ]) }) }) - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('user attempts to invite themselves', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + describe('user with undefined permission attempts to invite a user', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { - userName: "test.account@istio.actually.exists" + userName: "test@email.gc.ca" requestedRole: USER - orgId: "${toGlobalId('organizations', 1)}" + orgId: "${toGlobalId('organizations', 123)}" } ) { result { @@ -2793,71 +1391,74 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, + rootValue: null, + contextValue: { + i18n, + request: { + language: 'fr', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue(undefined), + tokenize, + userRequired: jest.fn().mockReturnValue({ + userName: 'test.account@exists.ca', + }), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadOrgByKey: { + load: jest.fn().mockReturnValue({ _key: 123 }), }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), + loadUserByKey: { + load: jest.fn(), }, - loaders: { - loaders: { - loadOrgByKey: { - load: jest.fn(), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, - }, + loadUserByUserName: { + load: jest.fn(), + }, + loadOrganizationNamesById: { + load: jest.fn(), }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, }, - }) + notify: { sendOrgInviteCreateAccount: jest.fn() }, + validators: { cleanseInput }, + }, + }) - const error = { - data: { - inviteUserToOrg: { - result: { - code: 400, - description: 'Unable to invite yourself to an org.', - }, + const error = { + data: { + inviteUserToOrg: { + result: { + code: 403, + description: 'Permission Denied: Please contact organization admin for help with user invitations.', }, }, - } + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([`User: 123 attempted to invite themselves to 1.`]) - }) + expect(response).toEqual(error) + expect(consoleOutput).toEqual([ + `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: user but does not have permission to do so.`, + ]) }) - describe('user attempts to invite to an org that does not exist', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + }) + describe('user with user level permission attempts to invite a user', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { userName: "test@email.gc.ca" requestedRole: USER - orgId: "${toGlobalId('organizations', 1)}" + orgId: "${toGlobalId('organizations', 123)}" } ) { result { @@ -2872,70 +1473,73 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, + rootValue: null, + contextValue: { + i18n, + request: { + language: 'fr', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('user'), + tokenize, + userRequired: jest.fn().mockReturnValue({ + userName: 'test.account@exists.ca', + }), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadOrgByKey: { + load: jest.fn().mockReturnValue({ _key: 123 }), }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), + loadUserByKey: { + load: jest.fn(), }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, + loadUserByUserName: { + load: jest.fn(), + }, + loadOrganizationNamesById: { + load: jest.fn(), }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, }, - }) + notify: { sendOrgInviteCreateAccount: jest.fn() }, + validators: { cleanseInput }, + }, + }) - const error = { - data: { - inviteUserToOrg: { - result: { - code: 400, - description: 'Unable to invite user to unknown organization.', - }, + const error = { + data: { + inviteUserToOrg: { + result: { + code: 403, + description: 'Permission Denied: Please contact organization admin for help with user invitations.', }, }, - } + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to 1 however there is no org associated with that id.`, - ]) - }) + expect(response).toEqual(error) + expect(consoleOutput).toEqual([ + `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: user but does not have permission to do so.`, + ]) }) - describe('user with undefined permission attempts to invite a user', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + }) + describe('user with admin level permission attempts to invite a user to super_admin permission', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { inviteUserToOrg( input: { userName: "test@email.gc.ca" - requestedRole: USER + requestedRole: SUPER_ADMIN orgId: "${toGlobalId('organizations', 123)}" } ) { @@ -2951,61 +1555,65 @@ describe('invite user to org', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, + rootValue: null, + contextValue: { + i18n, + request: { + language: 'fr', + protocol: 'https', + get: (text) => text, + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('admin'), + tokenize, + userRequired: jest.fn().mockReturnValue({ + userName: 'test.account@exists.ca', + }), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadOrgByKey: { + load: jest.fn().mockReturnValue({ _key: 123 }), }, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue(undefined), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), + loadUserByKey: { + load: jest.fn(), }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _key: 123 }), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, + loadUserByUserName: { + load: jest.fn(), + }, + loadOrganizationNamesById: { + load: jest.fn(), }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, }, - }) + notify: { sendOrgInviteCreateAccount: jest.fn() }, + validators: { cleanseInput }, + }, + }) - const error = { - data: { - inviteUserToOrg: { - result: { - code: 403, - description: 'Permission Denied: Please contact organization admin for help with user invitations.', - }, + const error = { + data: { + inviteUserToOrg: { + result: { + code: 403, + description: 'Permission Denied: Please contact super admin for help with user invitations.', }, }, - } + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: user but does not have permission to do so.`, - ]) - }) + expect(response).toEqual(error) + expect(consoleOutput).toEqual([ + `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: super_admin but does not have permission to do so.`, + ]) }) - describe('user with user level permission attempts to invite a user', () => { + }) + describe('transaction error occurs', () => { + describe('when creating affiliation', () => { it('returns an error message', async () => { const response = await graphql({ schema, @@ -3013,9 +1621,9 @@ describe('invite user to org', () => { mutation { inviteUserToOrg( input: { - userName: "test@email.gc.ca" + userName: "${userToInvite.userName}" requestedRole: USER - orgId: "${toGlobalId('organizations', 123)}" + orgId: "${toGlobalId('organizations', org._key)}" } ) { result { @@ -3040,10 +1648,13 @@ describe('invite user to org', () => { }, query, collections: collectionNames, - transaction, + transaction: jest.fn().mockReturnValue({ + step: jest.fn().mockRejectedValue(new Error('trx step err')), + abort: jest.fn(), + }), userKey: 123, auth: { - checkPermission: jest.fn().mockReturnValue('user'), + checkPermission: jest.fn().mockReturnValue('admin'), tokenize, userRequired: jest.fn().mockReturnValue({ userName: 'test.account@exists.ca', @@ -3052,15 +1663,10 @@ describe('invite user to org', () => { tfaRequired: jest.fn(), }, loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _key: 123 }), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, + loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, notify: { sendOrgInviteCreateAccount: jest.fn() }, validators: { cleanseInput }, @@ -3071,8 +1677,8 @@ describe('invite user to org', () => { data: { inviteUserToOrg: { result: { - code: 403, - description: 'Permission Denied: Please contact organization admin for help with user invitations.', + code: 500, + description: 'Unable to invite user. Please try again.', }, }, }, @@ -3080,11 +1686,11 @@ describe('invite user to org', () => { expect(response).toEqual(error) expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: user but does not have permission to do so.`, + `Transaction step error occurred while user: 123 attempted to invite user: ${userToInvite._key} to org: treasury-board-secretariat, error: Error: trx step err`, ]) }) }) - describe('user with admin level permission attempts to invite a user to super_admin permission', () => { + describe('when committing transaction', () => { it('returns an error message', async () => { const response = await graphql({ schema, @@ -3092,9 +1698,9 @@ describe('invite user to org', () => { mutation { inviteUserToOrg( input: { - userName: "test@email.gc.ca" - requestedRole: SUPER_ADMIN - orgId: "${toGlobalId('organizations', 123)}" + userName: "${userToInvite.userName}" + requestedRole: USER + orgId: "${toGlobalId('organizations', org._key)}" } ) { result { @@ -3119,7 +1725,11 @@ describe('invite user to org', () => { }, query, collections: collectionNames, - transaction, + transaction: jest.fn().mockReturnValue({ + step: jest.fn(), + commit: jest.fn().mockRejectedValue(new Error('trx commit err')), + abort: jest.fn(), + }), userKey: 123, auth: { checkPermission: jest.fn().mockReturnValue('admin'), @@ -3131,17 +1741,15 @@ describe('invite user to org', () => { tfaRequired: jest.fn(), }, loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _key: 123 }), - }, - loadUserByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, + loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), + }, + notify: { + sendOrgInviteCreateAccount: jest.fn(), + sendOrgInviteEmail: jest.fn(), }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, validators: { cleanseInput }, }, }) @@ -3150,8 +1758,8 @@ describe('invite user to org', () => { data: { inviteUserToOrg: { result: { - code: 403, - description: 'Permission Denied: Please contact organization admin for help with user invitations.', + code: 500, + description: 'Unable to invite user. Please try again.', }, }, }, @@ -3159,166 +1767,10 @@ describe('invite user to org', () => { expect(response).toEqual(error) expect(consoleOutput).toEqual([ - `User: 123 attempted to invite user: test@email.gc.ca to org: 123 with role: super_admin but does not have permission to do so.`, + `Transaction commit error occurred while user: 123 attempted to invite user: ${userToInvite._key} to org: treasury-board-secretariat, error: Error: trx commit err`, ]) }) }) - describe('transaction error occurs', () => { - describe('when creating affiliation', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "${userToInvite.userName}" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue('trx step err'), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { sendOrgInviteCreateAccount: jest.fn() }, - validators: { cleanseInput }, - }, - }) - - const error = { - data: { - inviteUserToOrg: { - result: { - code: 500, - description: 'Unable to invite user. Please try again.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred while user: 123 attempted to invite user: ${userToInvite._key} to org: treasury-board-secretariat, error: trx step err`, - ]) - }) - }) - describe('when committing transaction', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - inviteUserToOrg( - input: { - userName: "${userToInvite.userName}" - requestedRole: USER - orgId: "${toGlobalId('organizations', org._key)}" - } - ) { - result { - ... on InviteUserToOrgResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'fr', - protocol: 'https', - get: (text) => text, - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn(), - commit: jest.fn().mockRejectedValue('trx commit err'), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - tokenize, - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@exists.ca', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { - sendOrgInviteCreateAccount: jest.fn(), - sendOrgInviteEmail: jest.fn(), - }, - validators: { cleanseInput }, - }, - }) - - const error = { - data: { - inviteUserToOrg: { - result: { - code: 500, - description: 'Unable to invite user. Please try again.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction commit error occurred while user: 123 attempted to invite user: ${userToInvite._key} to org: treasury-board-secretariat, error: trx commit err`, - ]) - }) - }) - }) }) }) }) diff --git a/api/src/affiliation/mutations/__tests__/leave-organization.test.js b/api/src/affiliation/mutations/__tests__/leave-organization.test.js index 434943b4c..7a34fa2fb 100644 --- a/api/src/affiliation/mutations/__tests__/leave-organization.test.js +++ b/api/src/affiliation/mutations/__tests__/leave-organization.test.js @@ -1,6 +1,7 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as executeGraphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' @@ -11,11 +12,34 @@ import { loadUserByKey } from '../../../user/loaders' import { cleanseInput } from '../../../validators' import { createMutationSchema } from '../../../mutation' import { createQuerySchema } from '../../../query' +import { AffiliationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url, SIGN_IN_KEY } = process.env +const withAffiliationDataSource = (contextValue = {}) => { + if (contextValue.dataSources?.affiliation) return contextValue + + return { + ...contextValue, + dataSources: { + ...(contextValue.dataSources || {}), + affiliation: new AffiliationDataSource({ + query: contextValue.query, + transaction: contextValue.transaction, + collections: contextValue.collections, + userKey: contextValue.userKey, + i18n: contextValue.i18n, + language: contextValue.request?.language, + cleanseInput: contextValue.validators?.cleanseInput, + }), + }, + } +} + +const graphql = (args) => executeGraphql({ ...args, contextValue: withAffiliationDataSource(args.contextValue) }) + describe('given a successful leave', () => { let query, drop, truncate, schema, collections, transaction, i18n, user, org, domain, domain2 @@ -637,6 +661,7 @@ describe('given an unsuccessful leave', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Step error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -705,6 +730,7 @@ describe('given an unsuccessful leave', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(new Error('Step error occurred.')), commit: jest.fn().mockRejectedValue(new Error('Trx Commit Error')), + abort: jest.fn(), }) const response = await graphql({ @@ -848,6 +874,7 @@ describe('given an unsuccessful leave', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Step error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -916,6 +943,7 @@ describe('given an unsuccessful leave', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(new Error('Step error occurred.')), commit: jest.fn().mockRejectedValue(new Error('Trx Commit Error')), + abort: jest.fn(), }) const response = await graphql({ diff --git a/api/src/affiliation/mutations/__tests__/remove-user-from-org.test.js b/api/src/affiliation/mutations/__tests__/remove-user-from-org.test.js index d22a41d86..ba45c2efe 100644 --- a/api/src/affiliation/mutations/__tests__/remove-user-from-org.test.js +++ b/api/src/affiliation/mutations/__tests__/remove-user-from-org.test.js @@ -1,6 +1,7 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as executeGraphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' @@ -11,26 +12,53 @@ import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired, tfaRequired } from '../../../auth' import { loadOrgByKey } from '../../../organization/loaders' import { loadUserByKey } from '../../../user/loaders' -import { loadAffiliationByKey } from '../../loaders' +import { AffiliationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url } = process.env +const withAffiliationDataSource = (contextValue = {}) => { + const dataSources = contextValue.dataSources || {} + if (dataSources.affiliation && dataSources.auditLogs) return contextValue + + return { + ...contextValue, + dataSources: { + ...dataSources, + affiliation: + dataSources.affiliation || + new AffiliationDataSource({ + query: contextValue.query, + transaction: contextValue.transaction, + collections: contextValue.collections, + userKey: contextValue.userKey, + i18n: contextValue.i18n, + language: contextValue.request?.language, + cleanseInput: contextValue.validators?.cleanseInput, + }), + auditLogs: dataSources.auditLogs || { logActivity: jest.fn().mockResolvedValue(undefined) }, + }, + } +} + +const graphql = (args) => executeGraphql({ ...args, contextValue: withAffiliationDataSource(args.contextValue) }) + +const loadAffiliationWithDataSource = async ({ query, transaction, collections, userKey, i18n, affiliationKey }) => { + const affiliationDataSource = new AffiliationDataSource({ + query, + transaction, + collections, + userKey, + i18n, + cleanseInput, + }) + + return affiliationDataSource.byKey.load(affiliationKey) +} + const orgOneData = { verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, orgDetails: { en: { slug: 'treasury-board-secretariat', @@ -57,18 +85,6 @@ const orgOneData = { const orgTwoData = { verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, orgDetails: { en: { slug: 'communications-security-establishment', @@ -96,7 +112,6 @@ const orgTwoData = { const adminData = { userName: 'admin.account@istio.actually.exists', displayName: 'Test Admin', - preferredLang: 'french', tfaValidated: false, emailValidated: true, tfaSendMethod: 'email', @@ -105,7 +120,6 @@ const adminData = { const userData = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: true, tfaSendMethod: 'email', @@ -214,6 +228,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -249,14 +264,15 @@ describe('given the removeUserFromOrg mutation', () => { }, }) - const loader = loadAffiliationByKey({ + const data = await loadAffiliationWithDataSource({ query, + transaction, + collections, userKey: admin._key, i18n, + affiliationKey: affiliation._key, }) - const data = await loader.load(affiliation._key) - expect(consoleOutput).toEqual([ `User: ${admin._key} successfully removed user: ${user._key} from org: ${orgOne._key}.`, ]) @@ -311,6 +327,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -415,6 +432,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -506,6 +524,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -541,14 +560,15 @@ describe('given the removeUserFromOrg mutation', () => { }, }) - const loader = loadAffiliationByKey({ + const data = await loadAffiliationWithDataSource({ query, + transaction, + collections, userKey: admin._key, i18n, + affiliationKey: affiliation._key, }) - const data = await loader.load(affiliation._key) - expect(consoleOutput).toEqual([ `User: ${admin._key} successfully removed user: ${user._key} from org: ${orgOne._key}.`, ]) @@ -603,6 +623,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -707,6 +728,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -816,6 +838,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -851,14 +874,15 @@ describe('given the removeUserFromOrg mutation', () => { }, }) - const loader = loadAffiliationByKey({ + const data = await loadAffiliationWithDataSource({ query, + transaction, + collections, userKey: admin._key, i18n, + affiliationKey: affiliation._key, }) - const data = await loader.load(affiliation._key) - expect(consoleOutput).toEqual([ `User: ${admin._key} successfully removed user: ${user._key} from org: ${orgOne._key}.`, ]) @@ -913,6 +937,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -1017,6 +1042,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: admin._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ i18n, @@ -1126,6 +1152,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(), userRequired: jest.fn().mockReturnValue({ @@ -1206,6 +1233,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn().mockReturnValue({ @@ -1288,6 +1316,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn().mockReturnValue({ @@ -1370,6 +1399,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -1447,6 +1477,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('super_admin'), userRequired: jest.fn().mockReturnValue({ @@ -1525,6 +1556,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -1598,6 +1630,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -1640,6 +1673,7 @@ describe('given the removeUserFromOrg mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1675,6 +1709,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -1717,6 +1752,7 @@ describe('given the removeUserFromOrg mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1752,6 +1788,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -1834,6 +1871,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(), userRequired: jest.fn().mockReturnValue({ @@ -1914,6 +1952,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn().mockReturnValue({ @@ -1997,6 +2036,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn().mockReturnValue({ @@ -2080,6 +2120,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2157,6 +2198,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('super_admin'), userRequired: jest.fn().mockReturnValue({ @@ -2236,6 +2278,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2311,6 +2354,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2355,6 +2399,7 @@ describe('given the removeUserFromOrg mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -2390,6 +2435,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2434,6 +2480,7 @@ describe('given the removeUserFromOrg mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), }) const response = await graphql({ @@ -2469,6 +2516,7 @@ describe('given the removeUserFromOrg mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ diff --git a/api/src/affiliation/mutations/__tests__/request-org-affiliation.test.js b/api/src/affiliation/mutations/__tests__/request-org-affiliation.test.js index a2fbaa5ab..5016beaee 100644 --- a/api/src/affiliation/mutations/__tests__/request-org-affiliation.test.js +++ b/api/src/affiliation/mutations/__tests__/request-org-affiliation.test.js @@ -1,6 +1,7 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' -import { graphql, GraphQLSchema } from 'graphql' +import { graphql as executeGraphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' @@ -9,13 +10,40 @@ import { userRequired, verifiedRequired } from '../../../auth' import { createMutationSchema } from '../../../mutation' import { createQuerySchema } from '../../../query' import { cleanseInput } from '../../../validators' -import { loadOrgByKey } from '../../../organization/loaders' +import { AffiliationDataSource } from '../../data-source' +import { loadOrgByKey, loadOrganizationNamesById } from '../../../organization/loaders' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url, SIGN_IN_KEY } = process.env +const withAffiliationDataSource = (contextValue = {}) => { + const dataSources = contextValue.dataSources || {} + if (dataSources.affiliation && dataSources.auditLogs) return contextValue + + return { + ...contextValue, + dataSources: { + ...dataSources, + affiliation: + dataSources.affiliation || + new AffiliationDataSource({ + query: contextValue.query, + transaction: contextValue.transaction, + collections: contextValue.collections, + userKey: contextValue.userKey, + i18n: contextValue.i18n, + language: contextValue.request?.language, + cleanseInput: contextValue.validators?.cleanseInput, + }), + auditLogs: dataSources.auditLogs || { logActivity: jest.fn().mockResolvedValue(undefined) }, + }, + } +} + +const graphql = (args) => executeGraphql({ ...args, contextValue: withAffiliationDataSource(args.contextValue) }) + describe('invite user to org', () => { let query, drop, truncate, schema, collections, transaction, i18n, tokenize, user, org @@ -121,7 +149,6 @@ describe('invite user to org', () => { secondaryUser = await collections.users.save({ displayName: 'Test Account', userName: 'test@email.gc.ca', - preferredLang: 'english', }) await collections.affiliations.save({ _from: org._id, @@ -172,6 +199,7 @@ describe('invite user to org', () => { loaders: { loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, notify: { sendInviteRequestEmail: sendInviteRequestEmail }, validators: { cleanseInput }, @@ -197,7 +225,6 @@ describe('invite user to org', () => { _type: 'user', displayName: 'Test Account', id: secondaryUser._key, - preferredLang: 'english', userName: 'test@email.gc.ca', ...secondaryUser, }, @@ -333,6 +360,9 @@ describe('invite user to org', () => { loadUserByKey: { load: jest.fn(), }, + loadOrganizationNamesById: { + load: jest.fn(), + }, }, notify: { sendInviteRequestEmail: jest.fn() }, validators: { cleanseInput }, @@ -407,6 +437,9 @@ describe('invite user to org', () => { loadUserByKey: { load: jest.fn(), }, + loadOrganizationNamesById: { + load: jest.fn(), + }, }, notify: { sendInviteRequestEmail: jest.fn() }, validators: { cleanseInput }, @@ -482,6 +515,9 @@ describe('invite user to org', () => { loadUserByKey: { load: jest.fn(), }, + loadOrganizationNamesById: { + load: jest.fn(), + }, }, notify: { sendInviteRequestEmail: jest.fn() }, validators: { cleanseInput }, @@ -537,7 +573,7 @@ describe('invite user to org', () => { query, collections: collectionNames, transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue('trx step err'), + step: jest.fn().mockRejectedValue(new Error('trx step err')), }), userKey: 123, auth: { @@ -551,6 +587,7 @@ describe('invite user to org', () => { loaders: { loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), loadUserByKey: loadUserByKey({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, notify: { sendInviteRequestEmail: jest.fn() }, validators: { cleanseInput }, @@ -558,7 +595,7 @@ describe('invite user to org', () => { }) expect(consoleOutput).toEqual([ - `Transaction step error occurred while user: 123 attempted to request invite to org: treasury-board-secretariat, error: trx step err`, + `Transaction step error occurred while user: 123 attempted to request invite to org: treasury-board-secretariat, error: Error: trx step err`, ]) }) }) @@ -591,7 +628,7 @@ describe('invite user to org', () => { query, collections: collectionNames, transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue('trx commit err'), + step: jest.fn().mockRejectedValue(new Error('trx commit err')), }), userKey: 123, auth: { @@ -605,6 +642,7 @@ describe('invite user to org', () => { loaders: { loadOrgByKey: loadOrgByKey({ query, language: i18n.locale }), loadUserByKey: loadUserByKey({ query }), + loadOrganizationNamesById: loadOrganizationNamesById({ query }), }, notify: { sendInviteRequestEmail: jest.fn() }, validators: { cleanseInput }, @@ -612,7 +650,7 @@ describe('invite user to org', () => { }) expect(consoleOutput).toEqual([ - `Transaction step error occurred while user: 123 attempted to request invite to org: treasury-board-secretariat, error: trx commit err`, + `Transaction step error occurred while user: 123 attempted to request invite to org: treasury-board-secretariat, error: Error: trx commit err`, ]) }) }) diff --git a/api/src/affiliation/mutations/__tests__/transfer-org-ownership.test.js b/api/src/affiliation/mutations/__tests__/transfer-org-ownership.test.js index 8a74b44ef..72a42e05f 100644 --- a/api/src/affiliation/mutations/__tests__/transfer-org-ownership.test.js +++ b/api/src/affiliation/mutations/__tests__/transfer-org-ownership.test.js @@ -1,6 +1,7 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as executeGraphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' @@ -11,11 +12,34 @@ import { loadUserByKey } from '../../../user/loaders' import { cleanseInput } from '../../../validators' import { createMutationSchema } from '../../../mutation' import { createQuerySchema } from '../../../query' +import { AffiliationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url, SIGN_IN_KEY } = process.env +const withAffiliationDataSource = (contextValue = {}) => { + if (contextValue.dataSources?.affiliation) return contextValue + + return { + ...contextValue, + dataSources: { + ...(contextValue.dataSources || {}), + affiliation: new AffiliationDataSource({ + query: contextValue.query, + transaction: contextValue.transaction, + collections: contextValue.collections, + userKey: contextValue.userKey, + i18n: contextValue.i18n, + language: contextValue.request?.language, + cleanseInput: contextValue.validators?.cleanseInput, + }), + }, + } +} + +const graphql = (args) => executeGraphql({ ...args, contextValue: withAffiliationDataSource(args.contextValue) }) + describe('given the transferOrgOwnership mutation', () => { let query, drop, truncate, schema, collections, transaction, i18n, user, user2, org @@ -812,6 +836,7 @@ describe('given the transferOrgOwnership mutation', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Step Error')), + abort: jest.fn(), }) const response = await graphql({ @@ -839,7 +864,10 @@ describe('given the transferOrgOwnership mutation', () => { rootValue: null, contextValue: { i18n, - query: jest.fn().mockReturnValue({ count: 1 }), + query: jest.fn().mockReturnValue({ + count: 1, + next: jest.fn().mockReturnValue({ _key: 'affiliation-1' }), + }), collections: collectionNames, transaction: mockedTransaction, userKey: user._key, @@ -879,6 +907,7 @@ describe('given the transferOrgOwnership mutation', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('Step Error')), + abort: jest.fn(), }) const response = await graphql({ @@ -906,7 +935,10 @@ describe('given the transferOrgOwnership mutation', () => { rootValue: null, contextValue: { i18n, - query: jest.fn().mockReturnValue({ count: 1 }), + query: jest.fn().mockReturnValue({ + count: 1, + next: jest.fn().mockReturnValue({ _key: 'affiliation-1' }), + }), collections: collectionNames, transaction: mockedTransaction, userKey: user._key, @@ -948,6 +980,7 @@ describe('given the transferOrgOwnership mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('Commit Error')), + abort: jest.fn(), }) const response = await graphql({ @@ -975,7 +1008,10 @@ describe('given the transferOrgOwnership mutation', () => { rootValue: null, contextValue: { i18n, - query: jest.fn().mockReturnValue({ count: 1 }), + query: jest.fn().mockReturnValue({ + count: 1, + next: jest.fn().mockReturnValue({ _key: 'affiliation-1' }), + }), collections: collectionNames, transaction: mockedTransaction, userKey: user._key, @@ -1386,6 +1422,7 @@ describe('given the transferOrgOwnership mutation', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Step Error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1413,7 +1450,10 @@ describe('given the transferOrgOwnership mutation', () => { rootValue: null, contextValue: { i18n, - query: jest.fn().mockReturnValue({ count: 1 }), + query: jest.fn().mockReturnValue({ + count: 1, + next: jest.fn().mockReturnValue({ _key: 'affiliation-1' }), + }), collections: collectionNames, transaction: mockedTransaction, userKey: user._key, @@ -1455,6 +1495,7 @@ describe('given the transferOrgOwnership mutation', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('Step Error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1482,7 +1523,10 @@ describe('given the transferOrgOwnership mutation', () => { rootValue: null, contextValue: { i18n, - query: jest.fn().mockReturnValue({ count: 1 }), + query: jest.fn().mockReturnValue({ + count: 1, + next: jest.fn().mockReturnValue({ _key: 'affiliation-1' }), + }), collections: collectionNames, transaction: mockedTransaction, userKey: user._key, @@ -1526,6 +1570,7 @@ describe('given the transferOrgOwnership mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('Commit Error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1553,7 +1598,10 @@ describe('given the transferOrgOwnership mutation', () => { rootValue: null, contextValue: { i18n, - query: jest.fn().mockReturnValue({ count: 1 }), + query: jest.fn().mockReturnValue({ + count: 1, + next: jest.fn().mockReturnValue({ _key: 'affiliation-1' }), + }), collections: collectionNames, transaction: mockedTransaction, userKey: user._key, diff --git a/api/src/affiliation/mutations/__tests__/update-user-role.test.js b/api/src/affiliation/mutations/__tests__/update-user-role.test.js index 71f096391..bac77c338 100644 --- a/api/src/affiliation/mutations/__tests__/update-user-role.test.js +++ b/api/src/affiliation/mutations/__tests__/update-user-role.test.js @@ -1,6 +1,7 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { graphql as executeGraphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' @@ -11,11 +12,38 @@ import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired, tfaRequired } from '../../../auth' import { loadUserByUserName, loadUserByKey } from '../../../user/loaders' import { loadOrgByKey } from '../../../organization/loaders' +import { AffiliationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url } = process.env +const withAffiliationDataSource = (contextValue = {}) => { + const dataSources = contextValue.dataSources || {} + if (dataSources.affiliation && dataSources.auditLogs) return contextValue + + return { + ...contextValue, + dataSources: { + ...dataSources, + affiliation: + dataSources.affiliation || + new AffiliationDataSource({ + query: contextValue.query, + transaction: contextValue.transaction, + collections: contextValue.collections, + userKey: contextValue.userKey, + i18n: contextValue.i18n, + language: contextValue.request?.language, + cleanseInput: contextValue.validators?.cleanseInput, + }), + auditLogs: dataSources.auditLogs || { logActivity: jest.fn().mockResolvedValue(undefined) }, + }, + } +} + +const graphql = (args) => executeGraphql({ ...args, contextValue: withAffiliationDataSource(args.contextValue) }) + describe('update a users role', () => { let query, drop, truncate, schema, collections, transaction, i18n, user @@ -24,6 +52,12 @@ describe('update a users role', () => { const mockedWarn = (output) => consoleOutput.push(output) const mockedError = (output) => consoleOutput.push(output) + const sendRoleChangeEmail = jest.fn() + const orgNames = { + en: 'Treasury Board of Canada Secretariat', + fr: 'Secrétariat du Conseil Trésor du Canada', + } + beforeAll(async () => { console.info = mockedInfo console.warn = mockedWarn @@ -111,7 +145,6 @@ describe('update a users role', () => { secondaryUser = await collections.users.save({ displayName: 'Test Account', userName: 'test@email.gc.ca', - preferredLang: 'english', }) }) describe('requesting user is a super admin', () => { @@ -165,6 +198,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -177,10 +211,12 @@ describe('update a users role', () => { verifiedRequired: verifiedRequired({ i18n }), tfaRequired: tfaRequired({ i18n }), }, + notify: { sendRoleChangeEmail }, loaders: { loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: { load: jest.fn().mockResolvedValue(orgNames) }, }, validators: { cleanseInput, @@ -242,6 +278,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -254,10 +291,12 @@ describe('update a users role', () => { verifiedRequired: verifiedRequired({ i18n }), tfaRequired: tfaRequired({ i18n }), }, + notify: { sendRoleChangeEmail }, loaders: { loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: { load: jest.fn().mockResolvedValue(orgNames) }, }, validators: { cleanseInput, @@ -328,6 +367,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -340,10 +380,12 @@ describe('update a users role', () => { verifiedRequired: verifiedRequired({ i18n }), tfaRequired: tfaRequired({ i18n }), }, + notify: { sendRoleChangeEmail }, loaders: { loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: { load: jest.fn().mockResolvedValue(orgNames) }, }, validators: { cleanseInput, @@ -405,6 +447,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -417,10 +460,12 @@ describe('update a users role', () => { verifiedRequired: verifiedRequired({ i18n }), tfaRequired: tfaRequired({ i18n }), }, + notify: { sendRoleChangeEmail }, loaders: { loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: { load: jest.fn().mockResolvedValue(orgNames) }, }, validators: { cleanseInput, @@ -461,1397 +506,166 @@ describe('update a users role', () => { beforeEach(async () => { await collections.affiliations.save({ _from: org._id, - _to: secondaryUser._id, - permission: 'user', - }) - }) - describe('to admin', () => { - it('returns status message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', org._key)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - user { - displayName - } - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - validators: { - cleanseInput, - }, - }, - }) - - const expectedResponse = { - data: { - updateUserRole: { - result: { - status: 'User role was updated successfully.', - user: { - displayName: 'Test Account', - }, - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: ${user._key} successful updated user: ${secondaryUser._key} role to admin in org: treasury-board-secretariat.`, - ]) - expect(response).toEqual(expectedResponse) - }) - }) - }) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - let org, secondaryUser - beforeEach(async () => { - org = await collections.organizations.save({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }) - secondaryUser = await collections.users.save({ - displayName: 'Test Account', - userName: 'test@email.gc.ca', - preferredLang: 'english', - }) - }) - describe('requesting user is a super admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'super_admin', - }) - }) - describe('update user from admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: secondaryUser._id, - permission: 'admin', - }) - }) - describe('to super admin', () => { - it('returns status message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', org._key)}" - role: SUPER_ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - user { - displayName - } - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - validators: { - cleanseInput, - }, - }, - }) - - const expectedResponse = { - data: { - updateUserRole: { - result: { - status: "Le rôle de l'utilisateur a été mis à jour avec succès.", - user: { - displayName: 'Test Account', - }, - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: ${user._key} successful updated user: ${secondaryUser._key} role to super_admin in org: treasury-board-secretariat.`, - ]) - expect(response).toEqual(expectedResponse) - }) - }) - describe('to user', () => { - it('returns status message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', org._key)}" - role: USER - } - ) { - result { - ... on UpdateUserRoleResult { - status - user { - displayName - } - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - validators: { - cleanseInput, - }, - }, - }) - - const expectedResponse = { - data: { - updateUserRole: { - result: { - status: "Le rôle de l'utilisateur a été mis à jour avec succès.", - user: { - displayName: 'Test Account', - }, - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: ${user._key} successful updated user: ${secondaryUser._key} role to user in org: treasury-board-secretariat.`, - ]) - expect(response).toEqual(expectedResponse) - }) - }) - }) - describe('update user from user', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: secondaryUser._id, - permission: 'user', - }) - }) - describe('to super admin', () => { - it('returns status message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', org._key)}" - role: SUPER_ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - user { - displayName - } - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - validators: { - cleanseInput, - }, - }, - }) - - const expectedResponse = { - data: { - updateUserRole: { - result: { - status: "Le rôle de l'utilisateur a été mis à jour avec succès.", - user: { - displayName: 'Test Account', - }, - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: ${user._key} successful updated user: ${secondaryUser._key} role to super_admin in org: treasury-board-secretariat.`, - ]) - expect(response).toEqual(expectedResponse) - }) - }) - describe('to admin', () => { - it('returns status message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', org._key)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - user { - displayName - } - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - validators: { - cleanseInput, - }, - }, - }) - - const expectedResponse = { - data: { - updateUserRole: { - result: { - status: "Le rôle de l'utilisateur a été mis à jour avec succès.", - user: { - displayName: 'Test Account', - }, - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: ${user._key} successful updated user: ${secondaryUser._key} role to admin in org: treasury-board-secretariat.`, - ]) - expect(response).toEqual(expectedResponse) - }) - }) - }) - }) - describe('requesting user is admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'admin', - }) - }) - describe('update user from user', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: secondaryUser._id, - permission: 'user', - }) - }) - describe('to admin', () => { - it('returns status message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', org._key)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - user { - displayName - } - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({ i18n }), - tfaRequired: tfaRequired({ i18n }), - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - loadUserByUserName: loadUserByUserName({ query }), - }, - validators: { - cleanseInput, - }, - }, - }) - - const expectedResponse = { - data: { - updateUserRole: { - result: { - status: "Le rôle de l'utilisateur a été mis à jour avec succès.", - user: { - displayName: 'Test Account', - }, - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: ${user._key} successful updated user: ${secondaryUser._key} role to admin in org: treasury-board-secretariat.`, - ]) - expect(response).toEqual(expectedResponse) - }) - }) - }) - }) - }) - }) - describe('given an unsuccessful update', () => { - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given an unsuccessful role update', () => { - describe('user attempts to update their own role', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test.account@istio.actually.exists" - orgId: "${toGlobalId('organizations', 123)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn(), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn(), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = { - data: { - updateUserRole: { - result: { - code: 400, - description: 'Unable to update your own role.', - }, - }, - }, - } - - expect(consoleOutput).toEqual([`User: 123 attempted to update their own role in org: 123.`]) - expect(response).toEqual(error) - }) - }) - describe('user attempts to update a user that does not exist', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "random@email.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn(), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn(), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = { - data: { - updateUserRole: { - result: { - code: 400, - description: 'Unable to update role: user unknown.', - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: 123 attempted to update a user: random@email.ca role in org: 123, however there is no user associated with that user name.`, - ]) - expect(response).toEqual(error) - }) - }) - describe('user attempts to update a users role in an org that does not exist', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 1)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn(), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, - }), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = { - data: { - updateUserRole: { - result: { - code: 400, - description: 'Unable to update role: organization unknown.', - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: 123 attempted to update a user: 456 role in org: 1, however there is no org associated with that id.`, - ]) - expect(response).toEqual(error) - }) - }) - describe('requesting user permission is user', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('user'), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, - }), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = { - data: { - updateUserRole: { - result: { - code: 400, - description: - 'Permission Denied: Please contact organization admin for help with user role changes.', - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: 123 attempted to update a user: 456 role in org: treasury-board-secretariat, however they do not have permission to do so.`, - ]) - expect(response).toEqual(error) - }) - }) - describe('user attempts to update a users role in an org that the requesting user does not belong to', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ count: 0 }), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue(undefined), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, - }), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = { - data: { - updateUserRole: { - result: { - code: 400, - description: - 'Permission Denied: Please contact organization admin for help with user role changes.', - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: 123 attempted to update a user: 456 role in org: treasury-board-secretariat, however they do not have permission to do so.`, - ]) - expect(response).toEqual(error) - }) - }) - describe('user attempts to update a user that does not belong to the requested org', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ count: 0 }), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, - }), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = { - data: { - updateUserRole: { - result: { - code: 400, - description: 'Unable to update role: user does not belong to organization.', - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: 123 attempted to update a user: 456 role in org: treasury-board-secretariat, however that user does not have an affiliation with that organization.`, - ]) - expect(response).toEqual(error) - }) - }) - describe('requesting users role is admin', () => { - describe('requested users role is super admin', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: USER - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - count: 1, - next: jest.fn().mockReturnValue({ permission: 'super_admin' }), - }), - collections: collectionNames, - transaction: jest.fn(), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, - }), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = { - data: { - updateUserRole: { - result: { - code: 400, - description: - 'Permission Denied: Please contact organization admin for help with user role changes.', - }, - }, - }, - } - - expect(consoleOutput).toEqual([ - `User: 123 attempted to update a user: 456 role in org: treasury-board-secretariat, however they do not have permission to update a super_admin.`, - ]) - expect(response).toEqual(error) - }) - }) - }) - }) - describe('database error occurs', () => { - describe('when getting current affiliation', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: USER - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('database error')), - collections: collectionNames, - transaction: jest.fn(), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, - }), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] - - expect(consoleOutput).toEqual([ - `Database error occurred when user: 123 attempted to update a user's: 456 role, error: Error: database error`, - ]) - expect(response.errors).toEqual(error) - }) - }) - }) - describe('cursor error occur', () => { - describe('when gathering affiliation info', () => { - it('throws an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: USER - } - ) { - result { - ... on UpdateUserRoleResult { - status - } - ... on AffiliationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - count: 1, - next: jest.fn().mockRejectedValue(new Error('cursor error')), - }), - collections: collectionNames, - transaction: jest.fn(), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, - }), - }, - }, - validators: { - cleanseInput, - }, - }, - }) - - const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] - - expect(consoleOutput).toEqual([ - `Cursor error occurred when user: 123 attempted to update a user's: 456 role, error: Error: cursor error`, - ]) - expect(response.errors).toEqual(error) + _to: secondaryUser._id, + permission: 'user', + }) }) - }) - }) - describe('transaction error occurs', () => { - describe('when running transaction', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status + describe('to admin', () => { + it('returns status message', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateUserRole ( + input: { + userName: "test@email.gc.ca" + orgId: "${toGlobalId('organizations', org._key)}" + role: ADMIN } - ... on AffiliationError { - code - description + ) { + result { + ... on UpdateUserRoleResult { + status + user { + displayName + } + } + ... on AffiliationError { + code + description + } } } } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - count: 1, - next: jest.fn().mockReturnValue({ permission: 'user' }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue('trx step error'), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', + `, + rootValue: null, + contextValue: { + i18n, + query, + collections: collectionNames, + transaction, + userKey: user._key, + request: { ip: '127.0.0.1' }, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, + userRequired: userRequired({ + userKey: user._key, + loadUserByKey: loadUserByKey({ query }), }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + notify: { sendRoleChangeEmail }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: { load: jest.fn().mockResolvedValue(orgNames) }, + }, + validators: { + cleanseInput, }, }, - validators: { - cleanseInput, - }, - }, - }) + }) - const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] + const expectedResponse = { + data: { + updateUserRole: { + result: { + status: 'User role was updated successfully.', + user: { + displayName: 'Test Account', + }, + }, + }, + }, + } - expect(consoleOutput).toEqual([ - `Transaction step error occurred when user: 123 attempted to update a user's: 456 role, error: trx step error`, - ]) - expect(response.errors).toEqual(error) - }) - }) - describe('when committing transaction', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateUserRole ( - input: { - userName: "test@email.gc.ca" - orgId: "${toGlobalId('organizations', 123)}" - role: ADMIN - } - ) { - result { - ... on UpdateUserRoleResult { - status + expect(consoleOutput).toEqual([ + `User: ${user._key} successful updated user: ${secondaryUser._key} role to admin in org: treasury-board-secretariat.`, + ]) + expect(response).toEqual(expectedResponse) + }) + it('sends a role update notification email with correct parameters', async () => { + await graphql({ + schema, + source: ` + mutation { + updateUserRole ( + input: { + userName: "test@email.gc.ca" + orgId: "${toGlobalId('organizations', org._key)}" + role: USER } - ... on AffiliationError { - code - description + ) { + result { + ... on UpdateUserRoleResult { + status + user { + displayName + } + } + ... on AffiliationError { + code + description + } } } } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - count: 1, - next: jest.fn().mockReturnValue({ permission: 'user' }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn(), - commit: jest.fn().mockRejectedValue('trx commit error'), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - }), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', + `, + rootValue: null, + contextValue: { + i18n, + query, + collections: collectionNames, + transaction, + userKey: user._key, + request: { ip: '127.0.0.1' }, + auth: { + checkPermission: checkPermission({ + userKey: user._key, + query, }), - }, - loadUserByUserName: { - load: jest.fn().mockReturnValue({ - _key: 456, + userRequired: userRequired({ + userKey: user._key, + loadUserByKey: loadUserByKey({ query }), }), + verifiedRequired: verifiedRequired({ i18n }), + tfaRequired: tfaRequired({ i18n }), + }, + notify: { sendRoleChangeEmail }, + loaders: { + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), + loadOrganizationNamesById: { load: jest.fn().mockResolvedValue(orgNames) }, + }, + validators: { + cleanseInput, }, }, - validators: { - cleanseInput, - }, - }, - }) - - const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] + }) - expect(consoleOutput).toEqual([ - `Transaction commit error occurred when user: 123 attempted to update a user's: 456 role, error: trx commit error`, - ]) - expect(response.errors).toEqual(error) + expect(sendRoleChangeEmail).toHaveBeenCalledWith({ + user: expect.any(Object), + newRole: 'user', + oldRole: expect.any(String), + orgNames, + }) + }) }) }) }) }) - describe('users language is set to french', () => { + }) + describe('given an unsuccessful update', () => { + describe('users language is set to english', () => { beforeAll(() => { i18n = setupI18n({ - locale: 'fr', + locale: 'en', localeData: { en: { plurals: {} }, fr: { plurals: {} }, @@ -1896,6 +710,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn().mockReturnValue({ @@ -1912,6 +727,7 @@ describe('update a users role', () => { load: jest.fn(), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, @@ -1923,7 +739,7 @@ describe('update a users role', () => { updateUserRole: { result: { code: 400, - description: 'Impossible de mettre à jour votre propre rôle.', + description: 'Unable to update your own role.', }, }, }, @@ -1965,6 +781,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn().mockReturnValue({ @@ -1973,6 +790,7 @@ describe('update a users role', () => { verifiedRequired: jest.fn(), tfaRequired: jest.fn(), }, + notify: { sendRoleChangeEmail }, loaders: { loadOrgByKey: { load: jest.fn(), @@ -1992,7 +810,7 @@ describe('update a users role', () => { updateUserRole: { result: { code: 400, - description: 'Impossible de mettre à jour le rôle : utilisateur inconnu.', + description: 'Unable to update role: user unknown.', }, }, }, @@ -2036,6 +854,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn().mockReturnValue({ @@ -2044,6 +863,7 @@ describe('update a users role', () => { verifiedRequired: jest.fn(), tfaRequired: jest.fn(), }, + notify: { sendRoleChangeEmail }, loaders: { loadOrgByKey: { load: jest.fn().mockReturnValue(undefined), @@ -2065,7 +885,7 @@ describe('update a users role', () => { updateUserRole: { result: { code: 400, - description: 'Impossible de mettre à jour le rôle : organisation inconnue.', + description: 'Unable to update role: organization unknown.', }, }, }, @@ -2109,6 +929,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn().mockReturnValue({ @@ -2129,6 +950,7 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, @@ -2139,9 +961,9 @@ describe('update a users role', () => { data: { updateUserRole: { result: { - code: 400, + code: 403, description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur les changements de rôle des utilisateurs.", + 'Permission Denied: Please contact organization admin for help with user role changes.', }, }, }, @@ -2185,6 +1007,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn().mockReturnValue({ @@ -2205,6 +1028,7 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, @@ -2215,9 +1039,9 @@ describe('update a users role', () => { data: { updateUserRole: { result: { - code: 400, + code: 403, description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur les changements de rôle des utilisateurs.", + 'Permission Denied: Please contact organization admin for help with user role changes.', }, }, }, @@ -2261,6 +1085,7 @@ describe('update a users role', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2281,6 +1106,7 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, @@ -2292,8 +1118,7 @@ describe('update a users role', () => { updateUserRole: { result: { code: 400, - description: - "Impossible de mettre à jour le rôle : l'utilisateur n'appartient pas à l'organisation.", + description: 'Unable to update role: user does not belong to organization.', }, }, }, @@ -2305,7 +1130,6 @@ describe('update a users role', () => { expect(response).toEqual(error) }) }) - describe('requesting users role is admin', () => { describe('requested users role is super admin', () => { it('returns an error message', async () => { @@ -2342,6 +1166,7 @@ describe('update a users role', () => { collections: collectionNames, transaction: jest.fn(), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2362,6 +1187,7 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, @@ -2372,16 +1198,15 @@ describe('update a users role', () => { data: { updateUserRole: { result: { - code: 400, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur les changements de rôle des utilisateurs.", + code: 403, + description: 'Permission Denied: Please contact super admin for help with user role changes.', }, }, }, } expect(consoleOutput).toEqual([ - `User: 123 attempted to update a user: 456 role in org: treasury-board-secretariat, however they do not have permission to update a super_admin.`, + `User: 123 attempted to update a user: 456 role in org: treasury-board-secretariat, however they do not have permission to update a super_admin or assign the user role.`, ]) expect(response).toEqual(error) }) @@ -2421,6 +1246,7 @@ describe('update a users role', () => { collections: collectionNames, transaction: jest.fn(), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2441,15 +1267,14 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, }, }) - const error = [ - new GraphQLError(`Impossible de mettre à jour le rôle de l'utilisateur. Veuillez réessayer.`), - ] + const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] expect(consoleOutput).toEqual([ `Database error occurred when user: 123 attempted to update a user's: 456 role, error: Error: database error`, @@ -2494,6 +1319,7 @@ describe('update a users role', () => { collections: collectionNames, transaction: jest.fn(), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2514,15 +1340,14 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, }, }) - const error = [ - new GraphQLError(`Impossible de mettre à jour le rôle de l'utilisateur. Veuillez réessayer.`), - ] + const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] expect(consoleOutput).toEqual([ `Cursor error occurred when user: 123 attempted to update a user's: 456 role, error: Error: cursor error`, @@ -2566,9 +1391,11 @@ describe('update a users role', () => { }), collections: collectionNames, transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue('trx step error'), + step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2589,18 +1416,17 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, }, }) - const error = [ - new GraphQLError(`Impossible de mettre à jour le rôle de l'utilisateur. Veuillez réessayer.`), - ] + const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] expect(consoleOutput).toEqual([ - `Transaction step error occurred when user: 123 attempted to update a user's: 456 role, error: trx step error`, + `Transaction step error occurred when user: 123 attempted to update a user's: 456 role, error: Error: trx step error`, ]) expect(response.errors).toEqual(error) }) @@ -2640,9 +1466,11 @@ describe('update a users role', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn(), - commit: jest.fn().mockRejectedValue('trx commit error'), + commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), }), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn().mockReturnValue({ @@ -2663,18 +1491,17 @@ describe('update a users role', () => { }), }, }, + notify: { sendRoleChangeEmail }, validators: { cleanseInput, }, }, }) - const error = [ - new GraphQLError(`Impossible de mettre à jour le rôle de l'utilisateur. Veuillez réessayer.`), - ] + const error = [new GraphQLError(`Unable to update user's role. Please try again.`)] expect(consoleOutput).toEqual([ - `Transaction commit error occurred when user: 123 attempted to update a user's: 456 role, error: trx commit error`, + `Transaction commit error occurred when user: 123 attempted to update a user's: 456 role, error: Error: trx commit error`, ]) expect(response.errors).toEqual(error) }) diff --git a/api/src/affiliation/mutations/invite-user-to-org.js b/api/src/affiliation/mutations/invite-user-to-org.js index 2232bde56..aede988dc 100644 --- a/api/src/affiliation/mutations/invite-user-to-org.js +++ b/api/src/affiliation/mutations/invite-user-to-org.js @@ -4,8 +4,8 @@ import { GraphQLEmailAddress } from 'graphql-scalars' import { t } from '@lingui/macro' import { inviteUserToOrgUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import { InvitationRoleEnums } from '../../enums' +import ac from '../../access-control' export const inviteUserToOrg = new mutationWithClientMutationId({ name: 'InviteUserToOrg', @@ -38,13 +38,12 @@ able to sign-up and be assigned to that organization in one mutation.`, args, { i18n, - query, request, - collections, - transaction, userKey, + dataSources: { affiliation: affiliationDataSource, auditLogs: auditLogsDataSource }, + request: { ip }, auth: { checkPermission, tokenize, userRequired, verifiedRequired, tfaRequired }, - loaders: { loadOrgByKey, loadUserByUserName }, + loaders: { loadOrgByKey, loadUserByUserName, loadOrganizationNamesById }, notify: { sendOrgInviteCreateAccount, sendOrgInviteEmail }, validators: { cleanseInput }, }, @@ -87,11 +86,7 @@ able to sign-up and be assigned to that organization in one mutation.`, const permission = await checkPermission({ orgId: org._id }) // Only admins, owners, and super admins may invite users to an org - // Only super admins may create owners and other super admins - if ( - (['user', 'admin'].includes(requestedRole) && !['admin', 'owner', 'super_admin'].includes(permission)) || - (['super_admin', 'owner'].includes(requestedRole) && permission !== 'super_admin') - ) { + if (!ac.can(permission).createOwn('affiliation').granted) { console.warn( `User: ${userKey} attempted to invite user: ${userName} to org: ${org._key} with role: ${requestedRole} but does not have permission to do so.`, ) @@ -102,29 +97,26 @@ able to sign-up and be assigned to that organization in one mutation.`, } } - // Get org names to use in email - let orgNamesCursor - try { - orgNamesCursor = await query` - LET org = DOCUMENT(organizations, ${org._id}) - RETURN { - "orgNameEN": org.orgDetails.en.name, - "orgNameFR": org.orgDetails.fr.name, - } - ` - } catch (err) { - console.error( - `Database error occurred when user: ${userKey} attempted to invite user: ${userName} to org: ${org._key}. Error while creating cursor for retrieving organization names. error: ${err}`, + // Only super admins may create owners and other super admins + const privilegedRoles = ac.getRoles().filter((r) => ac.can(r).deleteOwn('organization').granted) + if (privilegedRoles.includes(requestedRole) && !ac.can(permission).createAny('affiliation').granted) { + console.warn( + `User: ${userKey} attempted to invite user: ${userName} to org: ${org._key} with role: ${requestedRole} but does not have permission to do so.`, ) - throw new Error(i18n._(t`Unable to invite user to organization. Please try again.`)) + return { + _type: 'error', + code: 403, + description: i18n._(t`Permission Denied: Please contact super admin for help with user invitations.`), + } } + // Get org names to use in email let orgNames try { - orgNames = await orgNamesCursor.next() + orgNames = await loadOrganizationNamesById.load(org._id) } catch (err) { console.error( - `Cursor error occurred when user: ${userKey} attempted to invite user: ${userName} to org: ${org._key}. Error while retrieving organization names. error: ${err}`, + `Error occurred when user: ${userKey} attempted to invite user: ${userName} to org: ${org._key}. Error while retrieving organization names. error: ${err}`, ) throw new Error(i18n._(t`Unable to invite user to organization. Please try again.`)) } @@ -148,14 +140,12 @@ able to sign-up and be assigned to that organization in one mutation.`, }) console.info(`User: ${userKey} successfully invited user: ${userName} to the service, and org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogsDataSource.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'add', target: { @@ -176,14 +166,9 @@ able to sign-up and be assigned to that organization in one mutation.`, } // If account is found, check if already affiliated with org - let affiliationCursor + let affiliation try { - affiliationCursor = await query` - WITH affiliations, organizations, users - FOR v, e IN 1..1 INBOUND ${requestedUser._id} affiliations - FILTER e._from == ${org._id} - RETURN e - ` + affiliation = await affiliationDataSource.affiliationByOrgAndUser({ orgId: org._id, userId: requestedUser._id }) } catch (err) { console.error( `Database error occurred when user: ${userKey} attempted to invite user: ${requestedUser._key} to org: ${org.slug}, error: ${err}`, @@ -195,7 +180,7 @@ able to sign-up and be assigned to that organization in one mutation.`, } } - if (affiliationCursor.count > 0) { + if (typeof affiliation !== 'undefined') { // If affiliation is found, return error console.warn( `User: ${userKey} attempted to invite user: ${requestedUser._key} to org: ${org.slug} however they are already affiliated with that org.`, @@ -209,26 +194,18 @@ able to sign-up and be assigned to that organization in one mutation.`, // User is not affiliated with org, create affiliation - // Setup Transaction - const trx = await transaction(collections) - - // Create affiliation try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - INSERT { - _from: ${org._id}, - _to: ${requestedUser._id}, - permission: ${requestedRole}, - } INTO affiliations - `, - ) + await affiliationDataSource.createAffiliation({ orgId: org._id, userId: requestedUser._id, permission: requestedRole }) } catch (err) { - console.error( - `Transaction step error occurred while user: ${userKey} attempted to invite user: ${requestedUser._key} to org: ${org.slug}, error: ${err}`, - ) + if (err.affiliationDataSourceOp === 'trx-commit') { + console.error( + `Transaction commit error occurred while user: ${userKey} attempted to invite user: ${requestedUser._key} to org: ${org.slug}, error: ${err}`, + ) + } else { + console.error( + `Transaction step error occurred while user: ${userKey} attempted to invite user: ${requestedUser._key} to org: ${org.slug}, error: ${err}`, + ) + } return { _type: 'error', code: 500, @@ -242,29 +219,13 @@ able to sign-up and be assigned to that organization in one mutation.`, orgNameFR: orgNames.orgNameFR, }) - // Commit affiliation - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction commit error occurred while user: ${userKey} attempted to invite user: ${requestedUser._key} to org: ${org.slug}, error: ${err}`, - ) - return { - _type: 'error', - code: 500, - description: i18n._(t`Unable to invite user. Please try again.`), - } - } - console.info(`User: ${userKey} successfully invited user: ${requestedUser._key} to the org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogsDataSource.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'add', target: { diff --git a/api/src/affiliation/mutations/leave-organization.js b/api/src/affiliation/mutations/leave-organization.js index 8a8ffb4c7..152854afe 100644 --- a/api/src/affiliation/mutations/leave-organization.js +++ b/api/src/affiliation/mutations/leave-organization.js @@ -24,9 +24,7 @@ export const leaveOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, + dataSources: { affiliation: affiliationDataSource }, auth: { userRequired, verifiedRequired }, loaders: { loadOrgByKey }, validators: { cleanseInput }, @@ -49,31 +47,18 @@ export const leaveOrganization = new mutationWithClientMutationId({ } } - // Setup Trans action - const trx = await transaction(collections) - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - FOR v, e IN 1..1 OUTBOUND ${org._id} affiliations - FILTER e._to == ${user._id} - REMOVE { _key: e._key } IN affiliations - OPTIONS { waitForSync: true } - `, - ) + await affiliationDataSource.removeAffiliation({ orgId: org._id, userId: user._id }) } catch (err) { - console.error( - `Trx step error occurred when removing user: ${user._key} affiliation with org: ${org._key}: ${err}`, - ) - throw new Error(i18n._(t`Unable leave organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Trx commit error occurred when user: ${user._key} attempted to leave org: ${org._key}: ${err}`) + if (err.affiliationDataSourceOp === 'trx-step') { + console.error( + `Trx step error occurred when removing user: ${user._key} affiliation with org: ${org._key}: ${err}`, + ) + } else if (err.affiliationDataSourceOp === 'trx-commit') { + console.error( + `Trx commit error occurred when user: ${user._key} attempted to leave org: ${org._key}: ${err}`, + ) + } throw new Error(i18n._(t`Unable leave organization. Please try again.`)) } diff --git a/api/src/affiliation/mutations/remove-user-from-org.js b/api/src/affiliation/mutations/remove-user-from-org.js index 3b335f1bf..909399f5b 100644 --- a/api/src/affiliation/mutations/remove-user-from-org.js +++ b/api/src/affiliation/mutations/remove-user-from-org.js @@ -3,7 +3,7 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { removeUserFromOrgUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' +import ac from '../../access-control' export const removeUserFromOrg = new mutationWithClientMutationId({ name: 'RemoveUserFromOrg', @@ -30,10 +30,9 @@ export const removeUserFromOrg = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, + dataSources: { affiliation: affiliationDataSource, auditLogs: auditLogsDataSource }, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, loaders: { loadOrgByKey, loadUserByKey }, validators: { cleanseInput }, @@ -79,22 +78,23 @@ export const removeUserFromOrg = new mutationWithClientMutationId({ } // Get requested users current permission level - let affiliationCursor + let affiliation try { - affiliationCursor = await query` - WITH affiliations, organizations, users - FOR v, e IN 1..1 ANY ${requestedUser._id} affiliations - FILTER e._from == ${requestedOrg._id} - RETURN { _key: e._key, permission: e.permission } - ` + affiliation = await affiliationDataSource.affiliationByOrgAndUser({ orgId: requestedOrg._id, userId: requestedUser._id }) } catch (err) { - console.error( - `Database error occurred when user: ${userKey} attempted to check the current permission of user: ${requestedUser._key} to see if they could be removed: ${err}`, - ) + if (err.affiliationDataSourceOp === 'query') { + console.error( + `Database error occurred when user: ${userKey} attempted to check the current permission of user: ${requestedUser._key} to see if they could be removed: ${err}`, + ) + } else if (err.affiliationDataSourceOp === 'cursor') { + console.error( + `Cursor error occurred when user: ${userKey} attempted to check the current permission of user: ${requestedUser._key} to see if they could be removed: ${err}`, + ) + } throw new Error(i18n._(t`Unable to remove user from this organization. Please try again.`)) } - if (affiliationCursor.count < 1) { + if (typeof affiliation === 'undefined') { console.warn( `User: ${userKey} attempted to remove user: ${requestedUser._key}, but they do not have any affiliations to org: ${requestedOrg._key}.`, ) @@ -105,18 +105,8 @@ export const removeUserFromOrg = new mutationWithClientMutationId({ } } - let affiliation - try { - affiliation = await affiliationCursor.next() - } catch (err) { - console.error( - `Cursor error occurred when user: ${userKey} attempted to check the current permission of user: ${requestedUser._key} to see if they could be removed: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove user from this organization. Please try again.`)) - } - // Only admins, owners, and super admins can remove users - if (['admin', 'owner', 'super_admin'].includes(permission) === false) { + if (!ac.can(permission).deleteOwn('affiliation').granted) { console.warn( `User: ${userKey} attempted to remove user: ${requestedUser._key} from org: ${requestedOrg._key}, but they do not have the right permission.`, ) @@ -139,46 +129,28 @@ export const removeUserFromOrg = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - FOR aff IN affiliations - FILTER aff._from == ${requestedOrg._id} - FILTER aff._to == ${requestedUser._id} - REMOVE aff IN affiliations - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred when user: ${userKey} attempted to remove user: ${requestedUser._key} from org: ${requestedOrg._key}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove user from this organization. Please try again.`)) - } - try { - await trx.commit() + await affiliationDataSource.removeAffiliation({ orgId: requestedOrg._id, userId: requestedUser._id }) } catch (err) { - console.error( - `Trx commit error occurred when user: ${userKey} attempted to remove user: ${requestedUser._key} from org: ${requestedOrg._key}, error: ${err}`, - ) + if (err.affiliationDataSourceOp === 'trx-step') { + console.error( + `Trx step error occurred when user: ${userKey} attempted to remove user: ${requestedUser._key} from org: ${requestedOrg._key}, error: ${err}`, + ) + } else if (err.affiliationDataSourceOp === 'trx-commit') { + console.error( + `Trx commit error occurred when user: ${userKey} attempted to remove user: ${requestedUser._key} from org: ${requestedOrg._key}, error: ${err}`, + ) + } throw new Error(i18n._(t`Unable to remove user from this organization. Please try again.`)) } console.info(`User: ${userKey} successfully removed user: ${requestedUser._key} from org: ${requestedOrg._key}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogsDataSource.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'remove', target: { diff --git a/api/src/affiliation/mutations/request-org-affiliation.js b/api/src/affiliation/mutations/request-org-affiliation.js index 2ae2097ca..e4564aefb 100644 --- a/api/src/affiliation/mutations/request-org-affiliation.js +++ b/api/src/affiliation/mutations/request-org-affiliation.js @@ -3,7 +3,6 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { inviteUserToOrgUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' const { SERVICE_ACCOUNT_EMAIL } = process.env @@ -28,13 +27,12 @@ export const requestOrgAffiliation = new mutationWithClientMutationId({ args, { i18n, - query, request, - collections, - transaction, userKey, + dataSources: { affiliation: affiliationDataSource, auditLogs: auditLogsDataSource }, + request: { ip }, auth: { userRequired, verifiedRequired }, - loaders: { loadOrgByKey, loadUserByKey }, + loaders: { loadOrgByKey, loadUserByKey, loadOrganizationNamesById }, notify: { sendInviteRequestEmail }, validators: { cleanseInput }, }, @@ -60,13 +58,9 @@ export const requestOrgAffiliation = new mutationWithClientMutationId({ } // Check to see if user is already a member of the org - let affiliationCursor + let requestedAffiliation try { - affiliationCursor = await query` - FOR v, e IN 1..1 OUTBOUND ${org._id} affiliations - FILTER e._to == ${user._id} - RETURN e - ` + requestedAffiliation = await affiliationDataSource.affiliationByOrgAndUser({ orgId: org._id, userId: user._id }) } catch (err) { console.error( `Database error occurred when user: ${userKey} attempted to request invite to ${orgId}, error: ${err}`, @@ -74,8 +68,7 @@ export const requestOrgAffiliation = new mutationWithClientMutationId({ throw new Error(i18n._(t`Unable to request invite. Please try again.`)) } - if (affiliationCursor.count > 0) { - const requestedAffiliation = await affiliationCursor.next() + if (typeof requestedAffiliation !== 'undefined') { if (requestedAffiliation.permission === 'pending') { console.warn( `User: ${userKey} attempted to request invite to org: ${orgId} however they have already requested to join that org.`, @@ -99,93 +92,64 @@ export const requestOrgAffiliation = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - // Create pending affiliation try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - INSERT { - _from: ${org._id}, - _to: ${user._id}, - permission: "pending", - } INTO affiliations - `, - ) + await affiliationDataSource.createPendingAffiliation({ orgId: org._id, userId: user._id }) } catch (err) { - console.error( - `Transaction step error occurred while user: ${userKey} attempted to request invite to org: ${org.slug}, error: ${err}`, - ) + if (err.affiliationDataSourceOp === 'trx-commit') { + console.error( + `Transaction commit error occurred while user: ${userKey} attempted to request invite to org: ${org.slug}, error: ${err}`, + ) + } else { + console.error( + `Transaction step error occurred while user: ${userKey} attempted to request invite to org: ${org.slug}, error: ${err}`, + ) + } throw new Error(i18n._(t`Unable to request invite. Please try again.`)) } // get all org admins - let orgAdminsCursor - try { - orgAdminsCursor = await query` - WITH affiliations, organizations, users - FOR v, e IN 1..1 OUTBOUND ${org._id} affiliations - FILTER e.permission IN ["admin", "owner", "super_admin"] - RETURN v._key - ` - } catch (err) { - console.error( - `Database error occurred when user: ${userKey} attempted to request invite to ${orgId}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to request invite. Please try again.`)) - } - let orgAdmins try { - orgAdmins = await orgAdminsCursor.all() + orgAdmins = await affiliationDataSource.orgAdminUserKeys({ orgId: org._id }) } catch (err) { - console.error( - `Cursor error occurred when user: ${userKey} attempted to request invite to ${orgId}, error: ${err}`, - ) + if (err.affiliationDataSourceOp === 'cursor') { + console.error( + `Cursor error occurred when user: ${userKey} attempted to request invite to ${orgId}, error: ${err}`, + ) + } else { + console.error( + `Database error occurred when user: ${userKey} attempted to request invite to ${orgId}, error: ${err}`, + ) + } throw new Error(i18n._(t`Unable to request invite. Please try again.`)) } - if (typeof SERVICE_ACCOUNT_EMAIL !== 'undefined') - orgAdmins.push({ - userName: SERVICE_ACCOUNT_EMAIL, - displayName: 'Service Account', - preferredLang: 'en', - _key: 'service-account', - }) + if (typeof SERVICE_ACCOUNT_EMAIL !== 'undefined') orgAdmins.push('service-account') if (orgAdmins.length > 0) { // Get org names to use in email - let orgNamesCursor - try { - orgNamesCursor = await query` - LET org = DOCUMENT(organizations, ${org._id}) - RETURN { - "orgNameEN": org.orgDetails.en.name, - "orgNameFR": org.orgDetails.fr.name, - } - ` - } catch (err) { - console.error( - `Database error occurred when user: ${userKey} attempted to request invite to org: ${org._key}. Error while creating cursor for retrieving organization names. error: ${err}`, - ) - throw new Error(i18n._(t`Unable to request invite. Please try again.`)) - } let orgNames try { - orgNames = await orgNamesCursor.next() + orgNames = await loadOrganizationNamesById.load(org._id) } catch (err) { console.error( - `Cursor error occurred when user: ${userKey} attempted to request invite to org: ${org._key}. Error while retrieving organization names. error: ${err}`, + `Error occurred when user: ${userKey} attempted to request invite to org: ${org._key}. Error while retrieving organization names. error: ${err}`, ) throw new Error(i18n._(t`Unable to request invite. Please try again.`)) } const adminLink = `https://${request.get('host')}/admin/organizations` // send notification to org admins for (const userKey of orgAdmins) { - const adminUser = await loadUserByKey.load(userKey) + let adminUser + if (userKey === 'service-account') { + adminUser = { + userName: SERVICE_ACCOUNT_EMAIL, + displayName: 'Service Account', + _key: 'service-account', + } + } else adminUser = await loadUserByKey.load(userKey) + await sendInviteRequestEmail({ user: adminUser, orgNameEN: orgNames.orgNameEN, @@ -195,24 +159,12 @@ export const requestOrgAffiliation = new mutationWithClientMutationId({ } } - // Commit Transaction - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction commit error occurred while user: ${userKey} attempted to request invite to org: ${org.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to request invite. Please try again.`)) - } - console.info(`User: ${userKey} successfully requested invite to the org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogsDataSource.logActivity({ initiatedBy: { id: user._key, userName: user.userName, + ipAddress: ip, }, action: 'add', target: { diff --git a/api/src/affiliation/mutations/transfer-org-ownership.js b/api/src/affiliation/mutations/transfer-org-ownership.js index 72acd33b9..d4ae317a3 100644 --- a/api/src/affiliation/mutations/transfer-org-ownership.js +++ b/api/src/affiliation/mutations/transfer-org-ownership.js @@ -29,9 +29,7 @@ export const transferOrgOwnership = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, + dataSources: { affiliation: affiliationDataSource }, auth: { checkOrgOwner, userRequired, verifiedRequired }, loaders: { loadOrgByKey, loadUserByKey }, validators: { cleanseInput }, @@ -91,14 +89,12 @@ export const transferOrgOwnership = new mutationWithClientMutationId({ } // query db for requested user affiliation to org - let affiliationCursor + let requestedUserAffiliation try { - affiliationCursor = await query` - WITH affiliations, organizations, users - FOR v, e IN 1..1 OUTBOUND ${org._id} affiliations - FILTER e._to == ${requestedUser._id} - RETURN e - ` + requestedUserAffiliation = await affiliationDataSource.affiliationByOrgAndUser({ + orgId: org._id, + userId: requestedUser._id, + }) } catch (err) { console.error( `Database error occurred for user: ${requestingUser._key} when they were attempting to transfer org: ${org.slug} ownership to user: ${requestedUser._key}: ${err}`, @@ -107,7 +103,7 @@ export const transferOrgOwnership = new mutationWithClientMutationId({ } // check to see if requested user belongs to org - if (affiliationCursor.count < 1) { + if (typeof requestedUserAffiliation === 'undefined') { console.warn( `User: ${requestingUser._key} attempted to transfer org: ${org.slug} ownership to user: ${requestedUser._key} but they are not affiliated with the org.`, ) @@ -120,60 +116,22 @@ export const transferOrgOwnership = new mutationWithClientMutationId({ } } - // Setup Trans action - const trx = await transaction(collections) - - // remove current org owners role - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - FOR aff IN affiliations - FILTER aff._from == ${org._id} - FILTER aff._to == ${requestingUser._id} - UPDATE { _key: aff._key } WITH { - permission: "admin", - } IN affiliations - RETURN aff - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${requestingUser._key} when they were attempting to transfer org: ${org.slug} ownership to user: ${requestedUser._key}: ${err}`, - ) - throw new Error(i18n._(t`Unable to transfer organization ownership. Please try again.`)) - } - - // set new org owner - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - FOR aff IN affiliations - FILTER aff._from == ${org._id} - FILTER aff._to == ${requestedUser._id} - UPDATE { _key: aff._key } WITH { - permission: "owner", - } IN affiliations - RETURN aff - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${requestingUser._key} when they were attempting to transfer org: ${org.slug} ownership to user: ${requestedUser._key}: ${err}`, - ) - throw new Error(i18n._(t`Unable to transfer organization ownership. Please try again.`)) - } - - // commit changes to the db try { - await trx.commit() + await affiliationDataSource.transferOrgOwnership({ + orgId: org._id, + fromUserId: requestingUser._id, + toUserId: requestedUser._id, + }) } catch (err) { - console.error( - `Trx commit error occurred for user: ${requestingUser._key} when they were attempting to transfer org: ${org.slug} ownership to user: ${requestedUser._key}: ${err}`, - ) + if (err.affiliationDataSourceOp === 'trx-step') { + console.error( + `Trx step error occurred for user: ${requestingUser._key} when they were attempting to transfer org: ${org.slug} ownership to user: ${requestedUser._key}: ${err}`, + ) + } else if (err.affiliationDataSourceOp === 'trx-commit') { + console.error( + `Trx commit error occurred for user: ${requestingUser._key} when they were attempting to transfer org: ${org.slug} ownership to user: ${requestedUser._key}: ${err}`, + ) + } throw new Error(i18n._(t`Unable to transfer organization ownership. Please try again.`)) } diff --git a/api/src/affiliation/mutations/update-user-role.js b/api/src/affiliation/mutations/update-user-role.js index 53546295e..1660b990f 100644 --- a/api/src/affiliation/mutations/update-user-role.js +++ b/api/src/affiliation/mutations/update-user-role.js @@ -5,7 +5,7 @@ import { t } from '@lingui/macro' import { RoleEnums } from '../../enums' import { updateUserRoleUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' +import ac from '../../access-control' export const updateUserRole = new mutationWithClientMutationId({ name: 'UpdateUserRole', @@ -37,13 +37,13 @@ given organization.`, args, { i18n, - query, - collections, - transaction, userKey, + dataSources: { affiliation: affiliationDataSource, auditLogs: auditLogsDataSource }, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, - loaders: { loadOrgByKey, loadUserByUserName }, + loaders: { loadOrgByKey, loadUserByUserName, loadOrganizationNamesById }, validators: { cleanseInput }, + notify: { sendRoleChangeEmail }, }, ) => { // Cleanse Input @@ -99,35 +99,36 @@ given organization.`, const permission = await checkPermission({ orgId: org._id }) // Only admins, owners, and super admins can update a user's role - if (['admin', 'owner', 'super_admin'].includes(permission) === false) { + if (!ac.can(permission).updateOwn('affiliation').granted) { console.warn( `User: ${userKey} attempted to update a user: ${requestedUser._key} role in org: ${org.slug}, however they do not have permission to do so.`, ) return { _type: 'error', - code: 400, + code: 403, description: i18n._(t`Permission Denied: Please contact organization admin for help with user role changes.`), } } // Get user's current permission level - let affiliationCursor + let affiliation try { - affiliationCursor = await query` - WITH affiliations, organizations, users - FOR v, e IN 1..1 ANY ${requestedUser._id} affiliations - FILTER e._from == ${org._id} - RETURN { _key: e._key, permission: e.permission } - ` + affiliation = await affiliationDataSource.affiliationByOrgAndUser({ orgId: org._id, userId: requestedUser._id }) } catch (err) { - console.error( - `Database error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, - ) + if (err.affiliationDataSourceOp === 'query') { + console.error( + `Database error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, + ) + } else if (err.affiliationDataSourceOp === 'cursor') { + console.error( + `Cursor error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, + ) + } throw new Error(i18n._(t`Unable to update user's role. Please try again.`)) } - if (affiliationCursor.count < 1) { + if (typeof affiliation === 'undefined') { console.warn( `User: ${userKey} attempted to update a user: ${requestedUser._key} role in org: ${org.slug}, however that user does not have an affiliation with that organization.`, ) @@ -138,88 +139,62 @@ given organization.`, } } - let affiliation - try { - affiliation = await affiliationCursor.next() - } catch (err) { - console.error( - `Cursor error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, - ) - - throw new Error(i18n._(t`Unable to update user's role. Please try again.`)) - } - - // Only super admins can update other super admins or owners - if (['owner', 'super_admin'].includes(affiliation.permission) && permission !== 'super_admin') { - console.warn( - `User: ${userKey} attempted to update a user: ${requestedUser._key} role in org: ${org.slug}, however they do not have permission to update a ${affiliation.permission}.`, - ) - return { - _type: 'error', - code: 400, - description: i18n._(t`Permission Denied: Please contact organization admin for help with user role changes.`), - } - } - - // Only super admins can make other users super admins or owners - if (['owner', 'super_admin'].includes(role) && permission !== 'super_admin') { + // Only super admins can update or assign privileged roles (those with org-level authority) + const privilegedRoles = ac.getRoles().filter((r) => ac.can(r).deleteOwn('organization').granted) + if ( + (privilegedRoles.includes(affiliation.permission) || privilegedRoles.includes(role)) && + !ac.can(permission).updateAny('affiliation').granted + ) { console.warn( - `User: ${userKey} attempted to update a user: ${requestedUser._key} role in org: ${org.slug}, however they do not have permission to make a user a ${role}.`, + `User: ${userKey} attempted to update a user: ${requestedUser._key} role in org: ${org.slug}, however they do not have permission to update a ${affiliation.permission} or assign the ${role} role.`, ) return { _type: 'error', - code: 400, - description: i18n._(t`Permission Denied: Please contact organization admin for help with user role changes.`), + code: 403, + description: i18n._(t`Permission Denied: Please contact super admin for help with user role changes.`), } } - // Only super admins can create new super admins - const edge = { - _from: org._id, - _to: requestedUser._id, - permission: role, - } - - // Setup Transaction - const trx = await transaction(collections) - try { - await trx.step(async () => { - await query` - WITH affiliations, organizations, users - UPSERT { _key: ${affiliation._key} } - INSERT ${edge} - UPDATE ${edge} - IN affiliations - ` + await affiliationDataSource.updateAffiliationPermission({ + affiliationKey: affiliation._key, + orgId: org._id, + userId: requestedUser._id, + permission: role, }) } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, - ) - + if (err.affiliationDataSourceOp === 'trx-step') { + console.error( + `Transaction step error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, + ) + } else if (err.affiliationDataSourceOp === 'trx-commit') { + console.warn( + `Transaction commit error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, + ) + } throw new Error(i18n._(t`Unable to update user's role. Please try again.`)) } + // Get org names to use in email + let orgNames try { - await trx.commit() + orgNames = await loadOrganizationNamesById.load(org._id) } catch (err) { - console.warn( - `Transaction commit error occurred when user: ${userKey} attempted to update a user's: ${requestedUser._key} role, error: ${err}`, + console.error( + `Error occurred when user: ${userKey} attempted to update a user's: ${userName} role. Error while retrieving organization names. error: ${err}`, ) - throw new Error(i18n._(t`Unable to update user's role. Please try again.`)) } + await sendRoleChangeEmail({ user: requestedUser, newRole: role, oldRole: affiliation.permission, orgNames }) + console.info(`User: ${userKey} successful updated user: ${requestedUser._key} role to ${role} in org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogsDataSource.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'update', target: { diff --git a/api/src/affiliation/objects/__tests__/affiliation.test.js b/api/src/affiliation/objects/__tests__/affiliation.test.js index b53fce64a..ea7daa0d1 100644 --- a/api/src/affiliation/objects/__tests__/affiliation.test.js +++ b/api/src/affiliation/objects/__tests__/affiliation.test.js @@ -61,7 +61,6 @@ describe('given the user affiliation object', () => { id: '1', displayName: 'Test Account', emailValidated: false, - preferredLang: 'french', tfaValidated: false, userName: 'test.account@istio.actually.exists', } diff --git a/api/src/affiliation/objects/__tests__/update-user-role-result.test.js b/api/src/affiliation/objects/__tests__/update-user-role-result.test.js index e213eca19..e4482a6f6 100644 --- a/api/src/affiliation/objects/__tests__/update-user-role-result.test.js +++ b/api/src/affiliation/objects/__tests__/update-user-role-result.test.js @@ -1,7 +1,7 @@ -import {GraphQLString} from 'graphql' +import { GraphQLString } from 'graphql' -import {userSharedType} from '../../../user/objects' -import {updateUserRoleResultType} from '../update-user-role-result' +import { userSharedType } from '../../../user/objects' +import { updateUserRoleResultType } from '../update-user-role-result' describe('given the updateUserRoleResultType object', () => { describe('testing the field definitions', () => { @@ -24,7 +24,7 @@ describe('given the updateUserRoleResultType object', () => { it('returns the resolved field', () => { const demoType = updateUserRoleResultType.getFields() - expect(demoType.status.resolve({status: 'status'})).toEqual('status') + expect(demoType.status.resolve({ status: 'status' })).toEqual('status') }) }) describe('testing the user resolver', () => { @@ -39,14 +39,11 @@ describe('given the updateUserRoleResultType object', () => { id: '1', displayName: 'Test Account', emailValidated: false, - preferredLang: 'french', tfaValidated: false, userName: 'test.account@istio.actually.exists', } - expect(demoType.user.resolve({user: expectedResult})).toEqual( - expectedResult, - ) + expect(demoType.user.resolve({ user: expectedResult })).toEqual(expectedResult) }) }) }) diff --git a/api/src/audit-logs/data-source.js b/api/src/audit-logs/data-source.js new file mode 100644 index 000000000..054ad8f25 --- /dev/null +++ b/api/src/audit-logs/data-source.js @@ -0,0 +1,39 @@ +import { loadAuditLogByKey, loadAuditLogsByOrgId } from './loaders' + +export class AuditLogsDataSource { + constructor({ query, userKey, cleanseInput, i18n, transaction, collections }) { + this._query = query + this._transaction = transaction + this._collections = collections + this.byKey = loadAuditLogByKey({ query, userKey, i18n }) + this.getConnectionsByOrgId = loadAuditLogsByOrgId({ query, userKey, cleanseInput, i18n }) + } + + async logActivity({ initiatedBy, action, target, reason = '' }) { + const auditLog = { + timestamp: new Date().toISOString(), + initiatedBy, + target, + action, + reason, + } + + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH auditLogs + INSERT ${auditLog} INTO auditLogs + RETURN MERGE({ id: NEW._key, _type: "auditLog" }, NEW) + `, + ) + await trx.commit() + } catch (err) { + console.error(`Transaction error occurred while attempting to log user action: ${err}`) + await trx.abort() + } + + return auditLog + } +} diff --git a/api/src/audit-logs/index.js b/api/src/audit-logs/index.js index a07660be2..b6bdbe9ee 100644 --- a/api/src/audit-logs/index.js +++ b/api/src/audit-logs/index.js @@ -1,3 +1,4 @@ +export * from './data-source' export * from './input' export * from './loaders' export * from './mutations' diff --git a/api/src/audit-logs/loaders/__tests__/load-audit-logs-by-org-id.test.js b/api/src/audit-logs/loaders/__tests__/load-audit-logs-by-org-id.test.js index 473255bed..769d96689 100644 --- a/api/src/audit-logs/loaders/__tests__/load-audit-logs-by-org-id.test.js +++ b/api/src/audit-logs/loaders/__tests__/load-audit-logs-by-org-id.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -44,7 +45,6 @@ describe('given the load log connection using org id function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -252,11 +252,7 @@ describe('given the load log connection using org id function', () => { describe('order direction is ASC', () => { it('returns logs in order', async () => { const logLoader = loadAuditLogByKey({ query }) - const expectedLogs = await logLoader.loadMany([ - log1._key, - log2._key, - log3._key, - ]) + const expectedLogs = await logLoader.loadMany([log1._key, log2._key, log3._key]) expectedLogs[0].id = expectedLogs[0]._key expectedLogs[1].id = expectedLogs[1]._key @@ -341,9 +337,7 @@ describe('given the load log connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `You must provide a \`first\` or \`last\` value to properly paginate the \`Log\` connection.`, - ), + new Error(`You must provide a \`first\` or \`last\` value to properly paginate the \`Log\` connection.`), ) } @@ -373,9 +367,7 @@ describe('given the load log connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `Passing both \`first\` and \`last\` to paginate the \`Log\` connection is not supported.`, - ), + new Error(`Passing both \`first\` and \`last\` to paginate the \`Log\` connection is not supported.`), ) } @@ -404,11 +396,7 @@ describe('given the load log connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` on the \`Log\` connection cannot be less than zero.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` on the \`Log\` connection cannot be less than zero.`)) } expect(consoleOutput).toEqual([ @@ -435,11 +423,7 @@ describe('given the load log connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` on the \`Log\` connection cannot be less than zero.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` on the \`Log\` connection cannot be less than zero.`)) } expect(consoleOutput).toEqual([ @@ -516,9 +500,7 @@ describe('given the load log connection using org id function', () => { describe('given a database error', () => { describe('when gathering log keys that are claimed by orgs that the user has affiliations to', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database Error Occurred.')) const connectionLoader = loadAuditLogsByOrgId({ query, @@ -537,9 +519,7 @@ describe('given the load log connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to query log(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to query log(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -575,9 +555,7 @@ describe('given the load log connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load log(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load log(s). Please try again.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/audit-logs/mutations/log-activity.js b/api/src/audit-logs/mutations/log-activity.js index fc59f53f1..46673cb5e 100644 --- a/api/src/audit-logs/mutations/log-activity.js +++ b/api/src/audit-logs/mutations/log-activity.js @@ -5,6 +5,7 @@ export const logActivity = async ({ initiatedBy = { id: '', userName: '', + ipAddress: '', // IP address of user role: '', // permission level of user organization: '', // org affiliation of user }, @@ -42,17 +43,15 @@ export const logActivity = async ({ `, ) } catch (err) { - console.error( - `Transaction step error occurred while attempting to log user action: ${err}`, - ) + console.error(`Transaction step error occurred while attempting to log user action: ${err}`) + await trx.abort() } try { await trx.commit() } catch (err) { - console.error( - `Transaction commit error occurred while attempting to log user action: ${err}`, - ) + console.error(`Transaction commit error occurred while attempting to log user action: ${err}`) + await trx.abort() } return auditLog diff --git a/api/src/audit-logs/objects/initiated-by.js b/api/src/audit-logs/objects/initiated-by.js index d47515250..9a1347e43 100644 --- a/api/src/audit-logs/objects/initiated-by.js +++ b/api/src/audit-logs/objects/initiated-by.js @@ -1,6 +1,6 @@ import { GraphQLObjectType, GraphQLString } from 'graphql' import { globalIdField } from 'graphql-relay' -import { GraphQLEmailAddress } from 'graphql-scalars' +import { GraphQLEmailAddress, GraphQLIP } from 'graphql-scalars' import { RoleEnums } from '../../enums' export const initiatedByType = new GraphQLObjectType({ @@ -13,6 +13,16 @@ export const initiatedByType = new GraphQLObjectType({ description: 'User email address.', resolve: ({ userName }) => userName, }, + ipAddress: { + type: GraphQLIP, + description: 'User IP address.', + resolve: async ({ ipAddress }, _args, { auth: { checkSuperAdmin, superAdminRequired } }) => { + const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ isSuperAdmin }) + + return ipAddress + }, + }, role: { type: RoleEnums, description: 'User permission level.', diff --git a/api/src/audit-logs/objects/target-resource.js b/api/src/audit-logs/objects/target-resource.js index 23af8022d..e89b43b32 100644 --- a/api/src/audit-logs/objects/target-resource.js +++ b/api/src/audit-logs/objects/target-resource.js @@ -40,8 +40,7 @@ export const targetResourceType = new GraphQLObjectType({ }, resourceType: { type: ResourceTypeEnums, - description: - 'Type of resource that was modified: user, domain, or organization.', + description: 'Type of resource that was modified: user, domain, or organization.', resolve: ({ resourceType }) => resourceType, }, updatedProperties: { @@ -58,14 +57,9 @@ export const targetResourceType = new GraphQLObjectType({ oldValue: { type: GraphQLString, description: 'Old value of updated property.', - resolve: ({ name, oldValue }, _, { language }) => { - if (name === 'selectors') { + resolve: ({ name, oldValue }) => { + if (name === 'selectors' || name === 'tags') { return JSON.stringify(oldValue) - } else if (name === 'tags') { - const translatedTags = oldValue.map((x) => { - return x[`${language}`] - }) - return JSON.stringify(translatedTags) } return oldValue }, @@ -73,14 +67,9 @@ export const targetResourceType = new GraphQLObjectType({ newValue: { type: GraphQLString, description: 'New value of updated property.', - resolve: ({ name, newValue }, _, { language }) => { - if (name === 'selectors') { + resolve: ({ name, newValue }) => { + if (name === 'selectors' || name === 'tags') { return JSON.stringify(newValue) - } else if (name === 'tags') { - const translatedTags = newValue.map((x) => { - return x[`${language}`] - }) - return JSON.stringify(translatedTags) } return newValue }, diff --git a/api/src/audit-logs/queries/find-audit-logs.js b/api/src/audit-logs/queries/find-audit-logs.js index 838c60a6c..b10186c11 100644 --- a/api/src/audit-logs/queries/find-audit-logs.js +++ b/api/src/audit-logs/queries/find-audit-logs.js @@ -4,6 +4,7 @@ import { logConnection } from '../objects/log-connection' import { logOrder } from '../input/log-order' import { t } from '@lingui/macro' import { logFilters } from '../input/log-filters' +import ac from '../../access-control' export const findAuditLogs = { type: logConnection.connectionType, @@ -34,7 +35,8 @@ export const findAuditLogs = { userKey, i18n, auth: { checkPermission, userRequired, verifiedRequired }, - loaders: { loadAuditLogsByOrgId, loadOrgByKey }, + loaders: { loadOrgByKey }, + dataSources: { auditLogs }, validators: { cleanseInput }, }, ) => { @@ -47,10 +49,10 @@ export const findAuditLogs = { // Check to see if user belongs to org const permission = await checkPermission({ orgId: org?._id }) - if (['admin', 'owner', 'super_admin'].includes(permission) === false) { + if (!ac.can(permission).readOwn('log').granted) { throw new Error(i18n._(t`Cannot query audit logs on organization without admin permission or higher.`)) } - const auditLogCollection = await loadAuditLogsByOrgId({ + const auditLogCollection = await auditLogs.getConnectionsByOrgId({ ...args, orgId: org?._key, permission, diff --git a/api/src/auth/__tests__/check-domain-ownership.test.js b/api/src/auth/checks/__tests__/check-domain-ownership.test.js similarity index 98% rename from api/src/auth/__tests__/check-domain-ownership.test.js rename to api/src/auth/checks/__tests__/check-domain-ownership.test.js index 122bfd0e4..d66f96673 100644 --- a/api/src/auth/__tests__/check-domain-ownership.test.js +++ b/api/src/auth/checks/__tests__/check-domain-ownership.test.js @@ -1,10 +1,11 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import { checkDomainOwnership } from '../index' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -49,7 +50,6 @@ describe('given the check domain ownership function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/auth/__tests__/check-domain-permission.test.js b/api/src/auth/checks/__tests__/check-domain-permission.test.js similarity index 97% rename from api/src/auth/__tests__/check-domain-permission.test.js rename to api/src/auth/checks/__tests__/check-domain-permission.test.js index d39b08167..0682857b0 100644 --- a/api/src/auth/__tests__/check-domain-permission.test.js +++ b/api/src/auth/checks/__tests__/check-domain-permission.test.js @@ -1,10 +1,11 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import { checkDomainPermission } from '../index' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -39,7 +40,6 @@ describe('given the check domain permission function', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/auth/__tests__/check-org-owner.test.js b/api/src/auth/checks/__tests__/check-org-owner.test.js similarity index 96% rename from api/src/auth/__tests__/check-org-owner.test.js rename to api/src/auth/checks/__tests__/check-org-owner.test.js index ade0b35e4..e2a97b2e8 100644 --- a/api/src/auth/__tests__/check-org-owner.test.js +++ b/api/src/auth/checks/__tests__/check-org-owner.test.js @@ -1,10 +1,11 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import { checkOrgOwner } from '../check-org-owner' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -28,7 +29,6 @@ describe('given the checkOrgOwner function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/auth/__tests__/check-permission.test.js b/api/src/auth/checks/__tests__/check-permission.test.js similarity index 98% rename from api/src/auth/__tests__/check-permission.test.js rename to api/src/auth/checks/__tests__/check-permission.test.js index 17c2a5296..73171b409 100644 --- a/api/src/auth/__tests__/check-permission.test.js +++ b/api/src/auth/checks/__tests__/check-permission.test.js @@ -1,10 +1,11 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import { checkPermission } from '..' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -38,7 +39,6 @@ describe('given the check permission function', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/auth/__tests__/check-super-admin.test.js b/api/src/auth/checks/__tests__/check-super-admin.test.js similarity index 83% rename from api/src/auth/__tests__/check-super-admin.test.js rename to api/src/auth/checks/__tests__/check-super-admin.test.js index fa82565e5..f355b27bc 100644 --- a/api/src/auth/__tests__/check-super-admin.test.js +++ b/api/src/auth/checks/__tests__/check-super-admin.test.js @@ -1,12 +1,13 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' -import {checkSuperAdmin} from '../check-super-admin' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import { checkSuperAdmin } from '../check-super-admin' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the check super admin function', () => { let query, drop, truncate, collections, i18n, user, org @@ -22,7 +23,7 @@ describe('given the check super admin function', () => { describe('given a successful call', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -38,7 +39,6 @@ describe('given the check super admin function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -155,8 +155,8 @@ describe('given the check super admin function', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -167,9 +167,7 @@ describe('given the check super admin function', () => { }) describe('given a database error', () => { it('raises an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) try { const testSuperAdmin = checkSuperAdmin({ @@ -179,9 +177,7 @@ describe('given the check super admin function', () => { }) await testSuperAdmin() } catch (err) { - expect(err).toEqual( - new Error('Unable to check permission. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to check permission. Please try again.')) } expect(consoleOutput).toEqual([ @@ -206,9 +202,7 @@ describe('given the check super admin function', () => { }) await testSuperAdmin() } catch (err) { - expect(err).toEqual( - new Error('Unable to check permission. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to check permission. Please try again.')) } expect(consoleOutput).toEqual([ @@ -222,8 +216,8 @@ describe('given the check super admin function', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -234,9 +228,7 @@ describe('given the check super admin function', () => { }) describe('given a database error', () => { it('raises an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) try { const testSuperAdmin = checkSuperAdmin({ @@ -246,11 +238,7 @@ describe('given the check super admin function', () => { }) await testSuperAdmin() } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de vérifier l'autorisation. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de vérifier l'autorisation. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -275,11 +263,7 @@ describe('given the check super admin function', () => { }) await testSuperAdmin() } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de vérifier l'autorisation. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de vérifier l'autorisation. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/auth/__tests__/check-user-belongs-to-org.test.js b/api/src/auth/checks/__tests__/check-user-belongs-to-org.test.js similarity index 94% rename from api/src/auth/__tests__/check-user-belongs-to-org.test.js rename to api/src/auth/checks/__tests__/check-user-belongs-to-org.test.js index 6957b0501..695669697 100644 --- a/api/src/auth/__tests__/check-user-belongs-to-org.test.js +++ b/api/src/auth/checks/__tests__/check-user-belongs-to-org.test.js @@ -1,10 +1,11 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import { checkUserBelongsToOrg } from '../check-user-belongs-to-org' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -28,7 +29,6 @@ describe('given the checkUserBelongsToOrg function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/auth/__tests__/check-user-is-admin-for-user.test.js b/api/src/auth/checks/__tests__/check-user-is-admin-for-user.test.js similarity index 86% rename from api/src/auth/__tests__/check-user-is-admin-for-user.test.js rename to api/src/auth/checks/__tests__/check-user-is-admin-for-user.test.js index ea9096615..2a14fe533 100644 --- a/api/src/auth/__tests__/check-user-is-admin-for-user.test.js +++ b/api/src/auth/checks/__tests__/check-user-is-admin-for-user.test.js @@ -1,12 +1,13 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' -import {checkUserIsAdminForUser} from '../index' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import { checkUserIsAdminForUser } from '../index' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the checkUserIsAdminForUser', () => { let query, drop, truncate, collections, i18n, user1, user2, org @@ -18,8 +19,8 @@ describe('given the checkUserIsAdminForUser', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -34,7 +35,7 @@ describe('given the checkUserIsAdminForUser', () => { describe('given a successful call', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -50,14 +51,12 @@ describe('given the checkUserIsAdminForUser', () => { user1 = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) user2 = await collections.users.save({ userName: 'test.account2@istio.actually.exists', displayName: 'Test Account2', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }) @@ -213,8 +212,8 @@ describe('given the checkUserIsAdminForUser', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -229,9 +228,7 @@ describe('given the checkUserIsAdminForUser', () => { const testCheck = checkUserIsAdminForUser({ i18n, userKey: user1._key, - query: jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')), + query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), }) try { @@ -239,9 +236,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error('Permission error, not an admin for this user.'), - ) + expect(err).toEqual(Error('Permission error, not an admin for this user.')) } expect(consoleOutput).toEqual([ `Database error when checking to see if user: ${user1._key} has super admin permission for user: test.account2@istio.actually.exists, error: Error: Database error occurred.`, @@ -268,9 +263,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error('Permission error, not an admin for this user.'), - ) + expect(err).toEqual(Error('Permission error, not an admin for this user.')) } expect(consoleOutput).toEqual([ `Database error when checking to see if user: ${user1._key} has admin permission for user: test.account2@istio.actually.exists, error: Error: Database error occurred.`, @@ -298,9 +291,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error('Permission error, not an admin for this user.'), - ) + expect(err).toEqual(Error('Permission error, not an admin for this user.')) } expect(consoleOutput).toEqual([ `Cursor error when checking to see if user: ${user1._key} has super admin permission for user: test.account2@istio.actually.exists, error: Error: Cursor error occurred.`, @@ -333,9 +324,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error('Permission error, not an admin for this user.'), - ) + expect(err).toEqual(Error('Permission error, not an admin for this user.')) } expect(consoleOutput).toEqual([ `Cursor error when checking to see if user: ${user1._key} has admin permission for user: test.account2@istio.actually.exists, error: Error: Cursor error occurred.`, @@ -349,8 +338,8 @@ describe('given the checkUserIsAdminForUser', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -365,9 +354,7 @@ describe('given the checkUserIsAdminForUser', () => { const testCheck = checkUserIsAdminForUser({ i18n, userKey: user1._key, - query: jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')), + query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), }) try { @@ -375,11 +362,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error( - "Erreur de permission, pas d'administrateur pour cet utilisateur.", - ), - ) + expect(err).toEqual(Error("Erreur de permission, pas d'administrateur pour cet utilisateur.")) } expect(consoleOutput).toEqual([ `Database error when checking to see if user: ${user1._key} has super admin permission for user: test.account2@istio.actually.exists, error: Error: Database error occurred.`, @@ -406,11 +389,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error( - "Erreur de permission, pas d'administrateur pour cet utilisateur.", - ), - ) + expect(err).toEqual(Error("Erreur de permission, pas d'administrateur pour cet utilisateur.")) } expect(consoleOutput).toEqual([ `Database error when checking to see if user: ${user1._key} has admin permission for user: test.account2@istio.actually.exists, error: Error: Database error occurred.`, @@ -438,11 +417,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error( - "Erreur de permission, pas d'administrateur pour cet utilisateur.", - ), - ) + expect(err).toEqual(Error("Erreur de permission, pas d'administrateur pour cet utilisateur.")) } expect(consoleOutput).toEqual([ `Cursor error when checking to see if user: ${user1._key} has super admin permission for user: test.account2@istio.actually.exists, error: Error: Cursor error occurred.`, @@ -475,11 +450,7 @@ describe('given the checkUserIsAdminForUser', () => { userName: 'test.account2@istio.actually.exists', }) } catch (err) { - expect(err).toEqual( - Error( - "Erreur de permission, pas d'administrateur pour cet utilisateur.", - ), - ) + expect(err).toEqual(Error("Erreur de permission, pas d'administrateur pour cet utilisateur.")) } expect(consoleOutput).toEqual([ `Cursor error when checking to see if user: ${user1._key} has admin permission for user: test.account2@istio.actually.exists, error: Error: Cursor error occurred.`, diff --git a/api/src/auth/check-domain-ownership.js b/api/src/auth/checks/check-domain-ownership.js similarity index 100% rename from api/src/auth/check-domain-ownership.js rename to api/src/auth/checks/check-domain-ownership.js diff --git a/api/src/auth/check-domain-permission.js b/api/src/auth/checks/check-domain-permission.js similarity index 100% rename from api/src/auth/check-domain-permission.js rename to api/src/auth/checks/check-domain-permission.js diff --git a/api/src/auth/check-org-owner.js b/api/src/auth/checks/check-org-owner.js similarity index 100% rename from api/src/auth/check-org-owner.js rename to api/src/auth/checks/check-org-owner.js diff --git a/api/src/auth/check-permission.js b/api/src/auth/checks/check-permission.js similarity index 100% rename from api/src/auth/check-permission.js rename to api/src/auth/checks/check-permission.js diff --git a/api/src/auth/check-super-admin.js b/api/src/auth/checks/check-super-admin.js similarity index 100% rename from api/src/auth/check-super-admin.js rename to api/src/auth/checks/check-super-admin.js diff --git a/api/src/auth/check-user-belongs-to-org.js b/api/src/auth/checks/check-user-belongs-to-org.js similarity index 100% rename from api/src/auth/check-user-belongs-to-org.js rename to api/src/auth/checks/check-user-belongs-to-org.js diff --git a/api/src/auth/check-user-is-admin-for-user.js b/api/src/auth/checks/check-user-is-admin-for-user.js similarity index 100% rename from api/src/auth/check-user-is-admin-for-user.js rename to api/src/auth/checks/check-user-is-admin-for-user.js diff --git a/api/src/auth/checks/index.js b/api/src/auth/checks/index.js new file mode 100644 index 000000000..0aaa93dda --- /dev/null +++ b/api/src/auth/checks/index.js @@ -0,0 +1,7 @@ +export * from './check-domain-ownership' +export * from './check-domain-permission' +export * from './check-org-owner' +export * from './check-permission' +export * from './check-super-admin' +export * from './check-user-belongs-to-org' +export * from './check-user-is-admin-for-user' diff --git a/api/src/auth/data-source.js b/api/src/auth/data-source.js new file mode 100644 index 000000000..67389d24b --- /dev/null +++ b/api/src/auth/data-source.js @@ -0,0 +1,9 @@ +import { loadPermissionByOrgId, loadDomainPermissionByDomainId, loadOrgOwnerByOrgId } from './loaders' + +export class AuthDataSource { + constructor({ query, userKey, i18n }) { + this.permissionByOrgId = loadPermissionByOrgId({ query, userKey, i18n }) + this.domainPermissionByDomainId = loadDomainPermissionByDomainId({ query, userKey, i18n }) + this.orgOwnerByOrgId = loadOrgOwnerByOrgId({ query, userKey, i18n }) + } +} diff --git a/api/src/auth/__tests__/tfa-required.test.js b/api/src/auth/guards/__tests__/tfa-required.test.js similarity index 77% rename from api/src/auth/__tests__/tfa-required.test.js rename to api/src/auth/guards/__tests__/tfa-required.test.js index 1d7a7e3b4..c48657cef 100644 --- a/api/src/auth/__tests__/tfa-required.test.js +++ b/api/src/auth/guards/__tests__/tfa-required.test.js @@ -1,8 +1,8 @@ -import {setupI18n} from '@lingui/core' +import { setupI18n } from '@lingui/core' -import {tfaRequired} from '../index' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' +import { tfaRequired } from '../index' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' describe('given the tfaRequired function', () => { let i18n, user @@ -23,7 +23,6 @@ describe('given the tfaRequired function', () => { user = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: true, tfaSendMethod: 'email', @@ -32,7 +31,7 @@ describe('given the tfaRequired function', () => { it('returns true', () => { const tfaFunc = tfaRequired({}) - const verifiedUser = tfaFunc({user}) + const verifiedUser = tfaFunc({ user }) expect(verifiedUser).toBe(true) }) @@ -42,8 +41,8 @@ describe('given the tfaRequired function', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -57,22 +56,19 @@ describe('given the tfaRequired function', () => { user = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, tfaSendMethod: 'none', } }) it('throws an error', () => { - const tfaFunc = tfaRequired({i18n}) + const tfaFunc = tfaRequired({ i18n }) try { - tfaFunc({user}) + tfaFunc({ user }) } catch (err) { expect(err).toEqual( - new Error( - 'Verification error. Please activate multi-factor authentication to access content.', - ), + new Error('Verification error. Please activate multi-factor authentication to access content.'), ) } }) @@ -83,8 +79,8 @@ describe('given the tfaRequired function', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['fr'], messages: { @@ -97,17 +93,16 @@ describe('given the tfaRequired function', () => { user = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, tfaSendMethod: 'none', } }) it.skip('throws an error', () => { - const tfaFunc = tfaRequired({i18n}) + const tfaFunc = tfaRequired({ i18n }) try { - tfaFunc({user}) + tfaFunc({ user }) } catch (err) { expect(err).toEqual( new Error( diff --git a/api/src/auth/__tests__/user-required.test.js b/api/src/auth/guards/__tests__/user-required.test.js similarity index 75% rename from api/src/auth/__tests__/user-required.test.js rename to api/src/auth/guards/__tests__/user-required.test.js index 96d63d02f..19c4a7271 100644 --- a/api/src/auth/__tests__/user-required.test.js +++ b/api/src/auth/guards/__tests__/user-required.test.js @@ -1,13 +1,14 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' -import {loadUserByKey, loadUserByUserName} from '../../user/loaders' -import {userRequired} from '../index' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import dbschema from '../../../database.json' +import { loadUserByKey, loadUserByUserName } from '../../../user/loaders' +import { userRequired } from '../index' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadUserByKey dataloader', () => { let query, drop, truncate, collections, i18n @@ -25,7 +26,7 @@ describe('given a loadUserByKey dataloader', () => { describe('given a successful call', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -41,7 +42,6 @@ describe('given a loadUserByKey dataloader', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -63,7 +63,7 @@ describe('given a loadUserByKey dataloader', () => { const testUserRequired = userRequired({ userKey: expectedUser._key, - loadUserByKey: loadUserByKey({query}), + loadUserByKey: loadUserByKey({ query }), }) const user = await testUserRequired() @@ -75,8 +75,8 @@ describe('given a loadUserByKey dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -91,18 +91,14 @@ describe('given a loadUserByKey dataloader', () => { const testUserRequired = userRequired({ i18n, userKey: undefined, - loadUserByKey: loadUserByKey({query}), + loadUserByKey: loadUserByKey({ query }), }) await testUserRequired() } catch (err) { - expect(err).toEqual( - new Error('Authentication error. Please sign in.'), - ) + expect(err).toEqual(new Error('Authentication error. Please sign in.')) } - expect(consoleOutput).toEqual([ - `User attempted to access controlled content, but userKey was undefined.`, - ]) + expect(consoleOutput).toEqual([`User attempted to access controlled content, but userKey was undefined.`]) }) }) describe('user cannot be found in database', () => { @@ -113,13 +109,11 @@ describe('given a loadUserByKey dataloader', () => { const testUserRequired = userRequired({ i18n, userKey: '1', - loadUserByKey: loadUserByKey({query}), + loadUserByKey: loadUserByKey({ query }), }) await testUserRequired() } catch (err) { - expect(err).toEqual( - new Error('Authentication error. Please sign in.'), - ) + expect(err).toEqual(new Error('Authentication error. Please sign in.')) } expect(consoleOutput).toEqual([ @@ -133,8 +127,8 @@ describe('given a loadUserByKey dataloader', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -149,18 +143,14 @@ describe('given a loadUserByKey dataloader', () => { const testUserRequired = userRequired({ i18n, userKey: undefined, - loadUserByKey: loadUserByKey({query}), + loadUserByKey: loadUserByKey({ query }), }) await testUserRequired() } catch (err) { - expect(err).toEqual( - new Error("Erreur d'authentification. Veuillez vous connecter."), - ) + expect(err).toEqual(new Error("Erreur d'authentification. Veuillez vous connecter.")) } - expect(consoleOutput).toEqual([ - `User attempted to access controlled content, but userKey was undefined.`, - ]) + expect(consoleOutput).toEqual([`User attempted to access controlled content, but userKey was undefined.`]) }) }) describe('user cannot be found in database', () => { @@ -171,13 +161,11 @@ describe('given a loadUserByKey dataloader', () => { const testUserRequired = userRequired({ i18n, userKey: '1', - loadUserByKey: loadUserByKey({query}), + loadUserByKey: loadUserByKey({ query }), }) await testUserRequired() } catch (err) { - expect(err).toEqual( - new Error("Erreur d'authentification. Veuillez vous connecter."), - ) + expect(err).toEqual(new Error("Erreur d'authentification. Veuillez vous connecter.")) } expect(consoleOutput).toEqual([ @@ -193,8 +181,8 @@ describe('given a loadUserByKey dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -221,9 +209,7 @@ describe('given a loadUserByKey dataloader', () => { }) await testUserRequired() } catch (err) { - expect(err).toEqual( - new Error('Authentication error. Please sign in.'), - ) + expect(err).toEqual(new Error('Authentication error. Please sign in.')) } expect(consoleOutput).toEqual([ @@ -237,8 +223,8 @@ describe('given a loadUserByKey dataloader', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -265,9 +251,7 @@ describe('given a loadUserByKey dataloader', () => { }) await testUserRequired() } catch (err) { - expect(err).toEqual( - new Error("Erreur d'authentification. Veuillez vous connecter."), - ) + expect(err).toEqual(new Error("Erreur d'authentification. Veuillez vous connecter.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/auth/__tests__/verified-required.test.js b/api/src/auth/guards/__tests__/verified-required.test.js similarity index 70% rename from api/src/auth/__tests__/verified-required.test.js rename to api/src/auth/guards/__tests__/verified-required.test.js index ce8aa7a8b..4ea31d45d 100644 --- a/api/src/auth/__tests__/verified-required.test.js +++ b/api/src/auth/guards/__tests__/verified-required.test.js @@ -1,8 +1,8 @@ -import {setupI18n} from '@lingui/core' +import { setupI18n } from '@lingui/core' -import {verifiedRequired} from '../index' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' +import { verifiedRequired } from '../index' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' describe('given the verifiedRequired function', () => { let i18n, user @@ -23,7 +23,6 @@ describe('given the verifiedRequired function', () => { user = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: true, } @@ -31,7 +30,7 @@ describe('given the verifiedRequired function', () => { it('returns true', () => { const verifiedFunc = verifiedRequired({}) - const verifiedUser = verifiedFunc({user}) + const verifiedUser = verifiedFunc({ user }) expect(verifiedUser).toBe(true) }) @@ -41,8 +40,8 @@ describe('given the verifiedRequired function', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -56,21 +55,18 @@ describe('given the verifiedRequired function', () => { user = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, } }) it('throws an error', () => { - const verifiedFunc = verifiedRequired({i18n}) + const verifiedFunc = verifiedRequired({ i18n }) try { - verifiedFunc({user}) + verifiedFunc({ user }) } catch (err) { expect(err).toEqual( - new Error( - 'Verification error. Please verify your account via email to access content.', - ), + new Error('Verification error. Please verify your account via email to access content.'), ) } }) @@ -81,8 +77,8 @@ describe('given the verifiedRequired function', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['fr'], messages: { @@ -95,21 +91,18 @@ describe('given the verifiedRequired function', () => { user = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, } }) it('throws an error', () => { - const verifiedFunc = verifiedRequired({i18n}) + const verifiedFunc = verifiedRequired({ i18n }) try { - verifiedFunc({user}) + verifiedFunc({ user }) } catch (err) { expect(err).toEqual( - new Error( - 'Erreur de vérification. Veuillez vérifier votre compte par e-mail pour accéder au contenu.', - ), + new Error('Erreur de vérification. Veuillez vérifier votre compte par e-mail pour accéder au contenu.'), ) } }) diff --git a/api/src/auth/guards/index.js b/api/src/auth/guards/index.js new file mode 100644 index 000000000..5a21e03fa --- /dev/null +++ b/api/src/auth/guards/index.js @@ -0,0 +1,4 @@ +export * from './super-admin-required' +export * from './tfa-required' +export * from './user-required' +export * from './verified-required' diff --git a/api/src/auth/super-admin-required.js b/api/src/auth/guards/super-admin-required.js similarity index 100% rename from api/src/auth/super-admin-required.js rename to api/src/auth/guards/super-admin-required.js diff --git a/api/src/auth/tfa-required.js b/api/src/auth/guards/tfa-required.js similarity index 100% rename from api/src/auth/tfa-required.js rename to api/src/auth/guards/tfa-required.js diff --git a/api/src/auth/user-required.js b/api/src/auth/guards/user-required.js similarity index 100% rename from api/src/auth/user-required.js rename to api/src/auth/guards/user-required.js diff --git a/api/src/auth/verified-required.js b/api/src/auth/guards/verified-required.js similarity index 100% rename from api/src/auth/verified-required.js rename to api/src/auth/guards/verified-required.js diff --git a/api/src/auth/index.js b/api/src/auth/index.js index beba36ce3..2f77a785a 100644 --- a/api/src/auth/index.js +++ b/api/src/auth/index.js @@ -1,14 +1,4 @@ -export * from './check-domain-ownership' -export * from './check-domain-permission' -export * from './check-org-owner' -export * from './check-permission' -export * from './check-super-admin' -export * from './check-user-belongs-to-org' -export * from './check-user-is-admin-for-user' -export * from './generate-jwt' -export * from './salted-hash' -export * from './super-admin-required' -export * from './user-required' -export * from './verified-required' -export * from './verify-jwt' -export * from './tfa-required' +export * from './checks' +export * from './guards' +export * from './utils' +export * from './data-source' diff --git a/api/src/auth/loaders/__tests__/load-domain-permission-by-domain-id.test.js b/api/src/auth/loaders/__tests__/load-domain-permission-by-domain-id.test.js new file mode 100644 index 000000000..60344802b --- /dev/null +++ b/api/src/auth/loaders/__tests__/load-domain-permission-by-domain-id.test.js @@ -0,0 +1,206 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' + +import { loadDomainPermissionByDomainId } from '../load-domain-permission-by-domain-id' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' + +const { DB_PASS: rootPass, DB_URL: url } = process.env + +describe('given the loadDomainPermissionByDomainId loader', () => { + let query, drop, truncate, collections, org, domain, i18n + + const consoleOutput = [] + const mockedError = (output) => consoleOutput.push(output) + + beforeAll(() => { + console.error = mockedError + }) + afterEach(() => { + consoleOutput.length = 0 + }) + + describe('given a successful domain permission load', () => { + let user + + beforeAll(async () => { + ;({ query, drop, truncate, collections } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + }) + + beforeEach(async () => { + user = await collections.users.save({ + userName: 'test.account@istio.actually.exists', + displayName: 'Test Account', + tfaValidated: false, + emailValidated: false, + }) + org = await collections.organizations.save({ + verified: false, + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + domain = await collections.domains.save({ + domain: 'test.gc.ca', + slug: 'test-gc-ca', + lastRan: null, + selectors: [], + }) + await collections.claims.save({ _from: org._id, _to: domain._id }) + }) + + afterEach(async () => { + await truncate() + }) + + afterAll(async () => { + await drop() + }) + + describe('when user is affiliated with an org that claims the domain', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'user', + }) + }) + + it('returns true', async () => { + const loader = loadDomainPermissionByDomainId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(domain._id) + expect(result).toBe(true) + }) + }) + + describe('when user has no affiliation with any org claiming the domain', () => { + it('returns false', async () => { + const loader = loadDomainPermissionByDomainId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(domain._id) + expect(result).toBe(false) + }) + }) + + describe('when loading multiple domain IDs in one batch', () => { + let domain2 + + beforeEach(async () => { + domain2 = await collections.domains.save({ + domain: 'test2.gc.ca', + slug: 'test2-gc-ca', + lastRan: null, + selectors: [], + }) + await collections.affiliations.save({ _from: org._id, _to: user._id, permission: 'user' }) + }) + + it('returns correct permissions for each domain in a single batch', async () => { + const loader = loadDomainPermissionByDomainId({ query, userKey: user._key, i18n: {} }) + const [perm1, perm2] = await loader.loadMany([domain._id, domain2._id]) + expect(perm1).toBe(true) + expect(perm2).toBe(false) + }) + }) + }) + + describe('given an unsuccessful domain permission load', () => { + describe('language is set to english', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + }) + + describe('database error on super admin check', () => { + it('throws an error', async () => { + const mockQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadDomainPermissionByDomainId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('domains/1')).rejects.toThrow( + 'Permission check error. Unable to request domain information.', + ) + + expect(consoleOutput).toEqual([ + `Database error when checking super admin permission for user: users/1: Error: Database error occurred.`, + ]) + }) + }) + + describe('database error on batch domain permission check', () => { + it('throws an error', async () => { + const mockQuery = jest + .fn() + .mockResolvedValueOnce({ count: undefined }) + .mockRejectedValue(new Error('Database error occurred.')) + + const loader = loadDomainPermissionByDomainId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('domains/1')).rejects.toThrow( + 'Permission check error. Unable to request domain information.', + ) + + expect(consoleOutput).toEqual([ + `Database error when checking domain permissions for user: users/1: Error: Database error occurred.`, + ]) + }) + }) + + describe('cursor error on batch domain permission check', () => { + it('throws an error', async () => { + const errorCursor = { + forEach() { + throw new Error('Cursor error occurred.') + }, + } + const mockQuery = jest + .fn() + .mockResolvedValueOnce({ count: undefined }) + .mockResolvedValue(errorCursor) + + const loader = loadDomainPermissionByDomainId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('domains/1')).rejects.toThrow( + 'Permission check error. Unable to request domain information.', + ) + + expect(consoleOutput).toEqual([ + `Cursor error when checking domain permissions for user: users/1: Error: Cursor error occurred.`, + ]) + }) + }) + }) + }) +}) diff --git a/api/src/auth/loaders/__tests__/load-org-owner-by-org-id.test.js b/api/src/auth/loaders/__tests__/load-org-owner-by-org-id.test.js new file mode 100644 index 000000000..1812c0c96 --- /dev/null +++ b/api/src/auth/loaders/__tests__/load-org-owner-by-org-id.test.js @@ -0,0 +1,200 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' + +import { loadOrgOwnerByOrgId } from '../load-org-owner-by-org-id' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' + +const { DB_PASS: rootPass, DB_URL: url } = process.env + +describe('given the loadOrgOwnerByOrgId loader', () => { + let query, drop, truncate, collections, user, org, i18n + + const consoleOutput = [] + const mockedError = (output) => consoleOutput.push(output) + + beforeAll(() => { + console.error = mockedError + }) + afterEach(() => { + consoleOutput.length = 0 + }) + + describe('given a successful org owner load', () => { + beforeAll(async () => { + ;({ query, drop, truncate, collections } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + }) + + beforeEach(async () => { + user = await collections.users.save({ + userName: 'test.account@istio.actually.exists', + displayName: 'Test Account', + tfaValidated: false, + emailValidated: false, + }) + org = await collections.organizations.save({ + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + }) + + afterEach(async () => { + await truncate() + }) + + afterAll(async () => { + await drop() + }) + + describe('when user is the owner', () => { + beforeEach(async () => { + await collections.affiliations.save({ _from: org._id, _to: user._id, permission: 'owner' }) + }) + + it('returns true', async () => { + const loader = loadOrgOwnerByOrgId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(org._id) + expect(result).toBe(true) + }) + }) + + describe('when user is not the owner', () => { + beforeEach(async () => { + await collections.affiliations.save({ _from: org._id, _to: user._id, permission: 'admin' }) + }) + + it('returns false', async () => { + const loader = loadOrgOwnerByOrgId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(org._id) + expect(result).toBe(false) + }) + }) + + describe('when user has no affiliation', () => { + it('returns false', async () => { + const loader = loadOrgOwnerByOrgId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(org._id) + expect(result).toBe(false) + }) + }) + + describe('when loading multiple org IDs in one batch', () => { + let org2 + + beforeEach(async () => { + org2 = await collections.organizations.save({ + orgDetails: { + en: { + slug: 'second-org', + acronym: 'SO', + name: 'Second Organization', + zone: 'FED', + sector: 'SO', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'deuxieme-org', + acronym: 'DO', + name: 'Deuxième Organisation', + zone: 'FED', + sector: 'DO', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + await collections.affiliations.save({ _from: org._id, _to: user._id, permission: 'owner' }) + await collections.affiliations.save({ _from: org2._id, _to: user._id, permission: 'admin' }) + }) + + it('returns correct ownership status for each org in a single batch', async () => { + const loader = loadOrgOwnerByOrgId({ query, userKey: user._key, i18n: {} }) + const [isOwner1, isOwner2] = await loader.loadMany([org._id, org2._id]) + expect(isOwner1).toBe(true) + expect(isOwner2).toBe(false) + }) + }) + }) + + describe('given an unsuccessful org owner load', () => { + describe('language is set to english', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + }) + + describe('database error occurs', () => { + it('throws an error', async () => { + const mockQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadOrgOwnerByOrgId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('organizations/1')).rejects.toThrow( + 'Unable to load owner information. Please try again.', + ) + + expect(consoleOutput).toEqual([ + `Database error when checking org ownership for user: 1: Error: Database error occurred.`, + ]) + }) + }) + + describe('cursor error occurs', () => { + it('throws an error', async () => { + const errorCursor = { + forEach() { + throw new Error('Cursor error occurred.') + }, + } + const mockQuery = jest.fn().mockResolvedValue(errorCursor) + const loader = loadOrgOwnerByOrgId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('organizations/1')).rejects.toThrow( + 'Unable to load owner information. Please try again.', + ) + + expect(consoleOutput).toEqual([ + `Cursor error when checking org ownership for user: 1: Error: Cursor error occurred.`, + ]) + }) + }) + }) + }) +}) diff --git a/api/src/auth/loaders/__tests__/load-permission-by-org-id.test.js b/api/src/auth/loaders/__tests__/load-permission-by-org-id.test.js new file mode 100644 index 000000000..145bf1fa7 --- /dev/null +++ b/api/src/auth/loaders/__tests__/load-permission-by-org-id.test.js @@ -0,0 +1,263 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' + +import { loadPermissionByOrgId } from '../load-permission-by-org-id' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import dbschema from '../../../../database.json' + +const { DB_PASS: rootPass, DB_URL: url } = process.env + +describe('given the loadPermissionByOrgId loader', () => { + let query, drop, truncate, collections, i18n + + const consoleOutput = [] + const mockedError = (output) => consoleOutput.push(output) + + beforeAll(() => { + console.error = mockedError + }) + afterEach(() => { + consoleOutput.length = 0 + }) + + describe('given a successful permission load', () => { + let user, org + + beforeAll(async () => { + ;({ query, drop, truncate, collections } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + }) + + beforeEach(async () => { + user = await collections.users.save({ + userName: 'test.account@istio.actually.exists', + displayName: 'Test Account', + tfaValidated: false, + emailValidated: false, + }) + org = await collections.organizations.save({ + verified: false, + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + }) + + afterEach(async () => { + await truncate() + }) + + afterAll(async () => { + await drop() + }) + + describe('when user is a super admin', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'super_admin', + }) + }) + + it('returns super_admin for all requested org IDs', async () => { + const loader = loadPermissionByOrgId({ query, userKey: user._key, i18n: {} }) + const [result] = await loader.loadMany([org._id]) + expect(result).toEqual('super_admin') + }) + }) + + describe('when user is an admin', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'admin', + }) + }) + + it('returns admin', async () => { + const loader = loadPermissionByOrgId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(org._id) + expect(result).toEqual('admin') + }) + }) + + describe('when user is a regular user', () => { + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'user', + }) + }) + + it('returns user', async () => { + const loader = loadPermissionByOrgId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(org._id) + expect(result).toEqual('user') + }) + }) + + describe('when user has no affiliation with the org', () => { + it('returns null', async () => { + const loader = loadPermissionByOrgId({ query, userKey: user._key, i18n: {} }) + const result = await loader.load(org._id) + expect(result).toBeNull() + }) + }) + + describe('when loading multiple org IDs in one batch', () => { + let org2 + + beforeEach(async () => { + org2 = await collections.organizations.save({ + verified: false, + orgDetails: { + en: { + slug: 'second-org', + acronym: 'SO', + name: 'Second Organization', + zone: 'FED', + sector: 'SO', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'deuxieme-org', + acronym: 'DO', + name: 'Deuxième Organisation', + zone: 'FED', + sector: 'DO', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + await collections.affiliations.save({ _from: org._id, _to: user._id, permission: 'admin' }) + await collections.affiliations.save({ _from: org2._id, _to: user._id, permission: 'user' }) + }) + + it('returns correct permissions for each org in a single batch', async () => { + const loader = loadPermissionByOrgId({ query, userKey: user._key, i18n: {} }) + const [perm1, perm2] = await loader.loadMany([org._id, org2._id]) + expect(perm1).toEqual('admin') + expect(perm2).toEqual('user') + }) + }) + }) + + describe('given an unsuccessful permission load', () => { + describe('language is set to english', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + }) + + describe('database error on super admin check', () => { + it('throws an error', async () => { + const mockQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadPermissionByOrgId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('organizations/1')).rejects.toThrow('Authentication error. Please sign in.') + + expect(consoleOutput).toEqual([ + `Database error when checking super admin permission for user: users/1: Error: Database error occurred.`, + ]) + }) + }) + + describe('cursor error on super admin check', () => { + it('throws an error', async () => { + const cursor = { + next() { + throw new Error('Cursor error occurred.') + }, + } + const mockQuery = jest.fn().mockResolvedValue(cursor) + const loader = loadPermissionByOrgId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('organizations/1')).rejects.toThrow('Unable to check permission. Please try again.') + + expect(consoleOutput).toEqual([ + `Cursor error when checking super admin permission for user: users/1: Error: Cursor error occurred.`, + ]) + }) + }) + + describe('database error on batch permission check', () => { + it('throws an error', async () => { + const mockQuery = jest + .fn() + .mockResolvedValueOnce({ next: () => undefined }) + .mockRejectedValue(new Error('Database error occurred.')) + + const loader = loadPermissionByOrgId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('organizations/1')).rejects.toThrow('Authentication error. Please sign in.') + + expect(consoleOutput).toEqual([ + `Database error when checking permissions for user: users/1: Error: Database error occurred.`, + ]) + }) + }) + + describe('cursor error on batch permission check', () => { + it('throws an error', async () => { + const errorCursor = { + forEach() { + throw new Error('Cursor error occurred.') + }, + } + const mockQuery = jest + .fn() + .mockResolvedValueOnce({ next: () => undefined }) + .mockResolvedValue(errorCursor) + + const loader = loadPermissionByOrgId({ query: mockQuery, userKey: '1', i18n }) + + await expect(loader.load('organizations/1')).rejects.toThrow('Unable to check permission. Please try again.') + + expect(consoleOutput).toEqual([ + `Cursor error when checking permissions for user: users/1: Error: Cursor error occurred.`, + ]) + }) + }) + }) + }) +}) diff --git a/api/src/auth/loaders/index.js b/api/src/auth/loaders/index.js new file mode 100644 index 000000000..ed64fc570 --- /dev/null +++ b/api/src/auth/loaders/index.js @@ -0,0 +1,3 @@ +export * from './load-permission-by-org-id' +export * from './load-domain-permission-by-domain-id' +export * from './load-org-owner-by-org-id' diff --git a/api/src/auth/loaders/load-domain-permission-by-domain-id.js b/api/src/auth/loaders/load-domain-permission-by-domain-id.js new file mode 100644 index 000000000..ce2f8b3ce --- /dev/null +++ b/api/src/auth/loaders/load-domain-permission-by-domain-id.js @@ -0,0 +1,65 @@ +import DataLoader from 'dataloader' +import { t } from '@lingui/macro' + +export const loadDomainPermissionByDomainId = ({ query, userKey, i18n }) => + new DataLoader(async (domainIds) => { + const userKeyString = `users/${userKey}` + + // Check super admin once for the whole batch (mirrors existing checkDomainPermission logic) + let superAdminCursor + try { + superAdminCursor = await query` + WITH affiliations, organizations, users + FOR v, e IN 1..1 ANY ${userKeyString} affiliations + FILTER e.permission == 'super_admin' + RETURN e._from + ` + } catch (err) { + console.error(`Database error when checking super admin permission for user: ${userKeyString}: ${err}`) + throw new Error(i18n._(t`Permission check error. Unable to request domain information.`)) + } + + if (superAdminCursor.count > 0) { + return domainIds.map(() => true) + } + + // Batch domain permission check across all domain IDs in one query + let cursor + try { + cursor = await query` + WITH affiliations, claims, domains, organizations, users + LET userAffiliations = ( + FOR v, e IN 1..1 ANY ${userKeyString} affiliations + FILTER e.permission != "pending" + RETURN v + ) + LET hasVerifiedOrgAffiliation = POSITION(userAffiliations[*].verified, true) + FOR domainId IN ${domainIds} + LET domainOrgClaims = ( + FOR v, e IN 1..1 ANY domainId claims + RETURN v + ) + LET domainBelongsToVerifiedOrg = POSITION(domainOrgClaims[*].verified, true) + LET affiliatedClaims = INTERSECTION(userAffiliations, domainOrgClaims) + RETURN { + domainId: domainId, + permitted: (domainBelongsToVerifiedOrg && hasVerifiedOrgAffiliation) || LENGTH(affiliatedClaims) > 0 + } + ` + } catch (err) { + console.error(`Database error when checking domain permissions for user: ${userKeyString}: ${err}`) + throw new Error(i18n._(t`Permission check error. Unable to request domain information.`)) + } + + const permMap = {} + try { + await cursor.forEach(({ domainId, permitted }) => { + permMap[domainId] = permitted + }) + } catch (err) { + console.error(`Cursor error when checking domain permissions for user: ${userKeyString}: ${err}`) + throw new Error(i18n._(t`Permission check error. Unable to request domain information.`)) + } + + return domainIds.map((id) => permMap[id] ?? false) + }) diff --git a/api/src/auth/loaders/load-org-owner-by-org-id.js b/api/src/auth/loaders/load-org-owner-by-org-id.js new file mode 100644 index 000000000..88232815d --- /dev/null +++ b/api/src/auth/loaders/load-org-owner-by-org-id.js @@ -0,0 +1,37 @@ +import DataLoader from 'dataloader' +import { t } from '@lingui/macro' + +export const loadOrgOwnerByOrgId = ({ query, userKey, i18n }) => + new DataLoader(async (orgIds) => { + const userIdString = `users/${userKey}` + + let cursor + try { + cursor = await query` + WITH affiliations, organizations, users + LET userId = ${userIdString} + FOR orgId IN ${orgIds} + LET isOwner = FIRST( + FOR v, e IN 1..1 OUTBOUND orgId affiliations + FILTER e._to == userId + RETURN e.permission == "owner" + ) + RETURN { orgId: orgId, isOwner: isOwner == true } + ` + } catch (err) { + console.error(`Database error when checking org ownership for user: ${userKey}: ${err}`) + throw new Error(i18n._(t`Unable to load owner information. Please try again.`)) + } + + const ownerMap = {} + try { + await cursor.forEach(({ orgId, isOwner }) => { + ownerMap[orgId] = isOwner + }) + } catch (err) { + console.error(`Cursor error when checking org ownership for user: ${userKey}: ${err}`) + throw new Error(i18n._(t`Unable to load owner information. Please try again.`)) + } + + return orgIds.map((id) => ownerMap[id] ?? false) + }) diff --git a/api/src/auth/loaders/load-permission-by-org-id.js b/api/src/auth/loaders/load-permission-by-org-id.js new file mode 100644 index 000000000..cccc12aac --- /dev/null +++ b/api/src/auth/loaders/load-permission-by-org-id.js @@ -0,0 +1,77 @@ +import DataLoader from 'dataloader' +import { t } from '@lingui/macro' + +export const loadPermissionByOrgId = ({ query, userKey, i18n }) => + new DataLoader(async (orgIds) => { + const userKeyString = `users/${userKey}` + + // Check for super admin once for the whole batch + let superAdminCursor + try { + superAdminCursor = await query` + WITH affiliations, organizations, users + FOR v, e IN 1..1 INBOUND ${userKeyString} affiliations + FILTER e.permission == "super_admin" + RETURN e.permission + ` + } catch (err) { + console.error(`Database error when checking super admin permission for user: ${userKeyString}: ${err}`) + throw new Error(i18n._(t`Authentication error. Please sign in.`)) + } + + let superAdminPermission + try { + superAdminPermission = await superAdminCursor.next() + } catch (err) { + console.error(`Cursor error when checking super admin permission for user: ${userKeyString}: ${err}`) + throw new Error(i18n._(t`Unable to check permission. Please try again.`)) + } + + if (superAdminPermission === 'super_admin') { + return orgIds.map(() => 'super_admin') + } + + // Batch permission check across all org IDs in one query + let cursor + try { + cursor = await query` + WITH affiliations, organizations, users + LET userAffiliations = ( + FOR v, e IN 1..1 ANY ${userKeyString} affiliations + FILTER e.permission != "pending" + RETURN v + ) + LET hasVerifiedOrgAffiliation = POSITION(userAffiliations[*].verified, true) + FOR orgId IN ${orgIds} + LET org = DOCUMENT(orgId) + LET orgIsVerified = org.verified + LET userOrgAffiliation = FIRST( + FOR v, e IN 1..1 ANY ${userKeyString} affiliations + FILTER e._from == orgId + RETURN e + ) + RETURN { + orgId: orgId, + permission: userOrgAffiliation.permission IN ["user", "admin", "owner", "super_admin"] ? userOrgAffiliation.permission + : (orgIsVerified && hasVerifiedOrgAffiliation) ? "user" + : userOrgAffiliation.permission == "pending" ? userOrgAffiliation + : null + } + ` + } catch (err) { + console.error(`Database error when checking permissions for user: ${userKeyString}: ${err}`) + throw new Error(i18n._(t`Authentication error. Please sign in.`)) + } + + const permMap = {} + try { + await cursor.forEach(({ orgId, permission }) => { + permMap[orgId] = permission + }) + } catch (err) { + console.error(`Cursor error when checking permissions for user: ${userKeyString}: ${err}`) + throw new Error(i18n._(t`Unable to check permission. Please try again.`)) + } + + return orgIds.map((id) => permMap[id] ?? null) + }) diff --git a/api/src/auth/__tests__/generate-jwt.test.js b/api/src/auth/utils/__tests__/generate-jwt.test.js similarity index 100% rename from api/src/auth/__tests__/generate-jwt.test.js rename to api/src/auth/utils/__tests__/generate-jwt.test.js diff --git a/api/src/auth/__tests__/salted-hash.test.js b/api/src/auth/utils/__tests__/salted-hash.test.js similarity index 100% rename from api/src/auth/__tests__/salted-hash.test.js rename to api/src/auth/utils/__tests__/salted-hash.test.js diff --git a/api/src/auth/__tests__/verify-jwt.test.js b/api/src/auth/utils/__tests__/verify-jwt.test.js similarity index 95% rename from api/src/auth/__tests__/verify-jwt.test.js rename to api/src/auth/utils/__tests__/verify-jwt.test.js index a24e851c4..f53cf66a7 100644 --- a/api/src/auth/__tests__/verify-jwt.test.js +++ b/api/src/auth/utils/__tests__/verify-jwt.test.js @@ -2,8 +2,8 @@ import jwt from 'jsonwebtoken' import {setupI18n} from '@lingui/core' import {verifyToken} from '../index' -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' const {AUTHENTICATED_KEY} = process.env diff --git a/api/src/auth/generate-jwt.js b/api/src/auth/utils/generate-jwt.js similarity index 100% rename from api/src/auth/generate-jwt.js rename to api/src/auth/utils/generate-jwt.js diff --git a/api/src/auth/utils/index.js b/api/src/auth/utils/index.js new file mode 100644 index 000000000..d6160f558 --- /dev/null +++ b/api/src/auth/utils/index.js @@ -0,0 +1,3 @@ +export * from './generate-jwt' +export * from './salted-hash' +export * from './verify-jwt' diff --git a/api/src/auth/salted-hash.js b/api/src/auth/utils/salted-hash.js similarity index 100% rename from api/src/auth/salted-hash.js rename to api/src/auth/utils/salted-hash.js diff --git a/api/src/auth/verify-jwt.js b/api/src/auth/utils/verify-jwt.js similarity index 100% rename from api/src/auth/verify-jwt.js rename to api/src/auth/utils/verify-jwt.js diff --git a/api/src/collection-names.js b/api/src/collection-names.js index 9c51219fe..7916ff531 100644 --- a/api/src/collection-names.js +++ b/api/src/collection-names.js @@ -2,6 +2,7 @@ export const collectionNames = [ 'users', 'organizations', 'domains', + 'guidanceTags', 'dkimGuidanceTags', 'dmarcGuidanceTags', 'spfGuidanceTags', @@ -28,4 +29,5 @@ export const collectionNames = [ 'selectors', 'domainsToSelectors', 'organizationSummaries', + 'tags', ] diff --git a/api/src/create-context.js b/api/src/create-context.js index 12fb14ae3..4f66bc71c 100644 --- a/api/src/create-context.js +++ b/api/src/create-context.js @@ -7,7 +7,18 @@ import jwt from 'jsonwebtoken' import { loadUserByKey } from './user/loaders' import { cleanseInput, decryptPhoneNumber, slugify } from './validators' import { initializeLoaders } from './initialize-loaders' +import { SummariesDataSource } from './summaries' +import { DnsScanDataSource } from './dns-scan' +import { WebScanDataSource } from './web-scan' +import { AuditLogsDataSource } from './audit-logs' +import { AdditionalFindingsDataSource } from './additional-findings' +import { GuidanceTagDataSource } from './guidance-tag' +import { OrganizationDataSource } from './organization' +import { TagsDataSource } from './tags' +import { DomainDataSource } from './domain' +import { AffiliationDataSource } from './affiliation' import { + AuthDataSource, checkDomainOwnership, checkDomainPermission, checkOrgOwner, @@ -31,8 +42,9 @@ import { sendOrgInviteCreateAccount, sendOrgInviteEmail, sendPasswordResetEmail, - sendTfaTextMsg, + sendUpdatedUserNameEmail, sendVerificationEmail, + sendRoleChangeEmail, } from './notify' export async function createContext({ @@ -125,12 +137,54 @@ export async function createContext({ }), sendOrgInviteEmail: sendOrgInviteEmail({ notifyClient, i18n }), sendPasswordResetEmail: sendPasswordResetEmail({ notifyClient, i18n }), - sendTfaTextMsg: sendTfaTextMsg({ notifyClient, i18n }), + sendUpdatedUserNameEmail: sendUpdatedUserNameEmail({ + notifyClient, + i18n, + }), sendVerificationEmail: sendVerificationEmail({ notifyClient, i18n }), + sendRoleChangeEmail: sendRoleChangeEmail({ notifyClient, i18n }), + }, + dataSources: { + auth: new AuthDataSource({ query, userKey, i18n }), + summaries: new SummariesDataSource({ query, userKey, cleanseInput, i18n }), + additionalFindings: new AdditionalFindingsDataSource({ query, userKey, i18n, language: request.language }), + auditLogs: new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }), + dnsScan: new DnsScanDataSource({ query, userKey, cleanseInput, i18n }), + guidanceTag: new GuidanceTagDataSource({ query, userKey, i18n, language: request.language, cleanseInput }), + organization: new OrganizationDataSource({ + query, + userKey, + i18n, + language: request.language, + cleanseInput, + loginRequiredBool, + transaction, + collections, + }), + tags: new TagsDataSource({ query, userKey, i18n, language: request.language, transaction, collections }), + webScan: new WebScanDataSource({ query, userKey, cleanseInput, i18n }), + domain: new DomainDataSource({ + query, + userKey, + i18n, + language: request.language, + cleanseInput, + loginRequiredBool, + transaction, + collections, + }), + affiliation: new AffiliationDataSource({ + query, + userKey, + i18n, + language: request.language, + cleanseInput, + transaction, + collections, + }), }, loaders: initializeLoaders({ query, - db, userKey, i18n, language: request.language, diff --git a/api/src/create-i18n.js b/api/src/create-i18n.js index 58340f928..863c8804d 100644 --- a/api/src/create-i18n.js +++ b/api/src/create-i18n.js @@ -1,4 +1,3 @@ -import { en, fr } from 'make-plural/plurals' import { setupI18n } from '@lingui/core' import englishMessages from './locale/en/messages' import frenchMessages from './locale/fr/messages' @@ -11,8 +10,7 @@ export const createI18n = (language = 'en') => { }, }) - i18n.loadLocaleData('en', { plurals: en }) - i18n.loadLocaleData('fr', { plurals: fr }) + i18n.load({ en: englishMessages.messages, fr: frenchMessages.messages }) i18n.activate(language) return i18n } diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-all-verified-rua-domains.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-all-verified-rua-domains.test.js new file mode 100644 index 000000000..dbb7fd37d --- /dev/null +++ b/api/src/dmarc-summaries/loaders/__tests__/load-all-verified-rua-domains.test.js @@ -0,0 +1,27 @@ +import { loadAllVerifiedRuaDomains } from '../load-all-verified-rua-domains' + +describe('loadAllVerifiedRuaDomains', () => { + let query, userKey, i18n + + beforeEach(() => { + query = jest.fn().mockReturnValue({ + all: jest.fn().mockResolvedValue([{ key: 'org1', domains: ['domain1', 'domain2'] }]), + }) + userKey = 'userKey' + i18n = { _: jest.fn().mockReturnValue('error message') } + }) + + it('returns the correct data when the database query is successful', async () => { + const loader = loadAllVerifiedRuaDomains({ query, userKey, i18n }) + const result = await loader() + expect(result).toEqual([{ key: 'org1', domains: ['domain1', 'domain2'] }]) + }) + + it('throws an error when the database query fails', async () => { + query = jest.fn().mockImplementation(() => { + throw new Error() + }) + const loader = loadAllVerifiedRuaDomains({ query, userKey, i18n }) + await expect(loader()).rejects.toThrow('error message') + }) +}) diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-dkim-failure-connections-by-sum-id.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-dkim-failure-connections-by-sum-id.test.js index 84cfd6ada..4bdaf7eda 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-dkim-failure-connections-by-sum-id.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-dkim-failure-connections-by-sum-id.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -12,15 +13,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the loadDkimFailConnectionsBySumId loader', () => { - let query, - drop, - truncate, - collections, - i18n, - user, - dmarcSummary, - dkimFailure1, - dkimFailure2 + let query, drop, truncate, collections, i18n, user, dmarcSummary, dkimFailure1, dkimFailure2 const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -36,20 +29,19 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { describe('given a successful load', () => { beforeEach(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -453,11 +445,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `DkimFailureTable` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `DkimFailureTable` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -483,11 +471,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `DkimFailureTable` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `DkimFailureTable` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -499,9 +483,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDkimFailConnectionsBySumId({ query, userKey: user._key, @@ -519,11 +501,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -535,9 +513,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDkimFailConnectionsBySumId({ query, userKey: user._key, @@ -555,11 +531,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -587,11 +559,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to load DKIM failure data. Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to load DKIM failure data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -602,9 +570,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) describe('given a database error occurs', () => { it('returns an error message', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadDkimFailConnectionsBySumId({ query: mockedQuery, @@ -622,9 +588,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DKIM failure data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DKIM failure data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -657,9 +621,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DKIM failure data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DKIM failure data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -826,9 +788,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro.', - ), + new Error('`first` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro.'), ) } @@ -856,9 +816,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`last` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro.', - ), + new Error('`last` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro.'), ) } @@ -871,9 +829,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDkimFailConnectionsBySumId({ query, userKey: user._key, @@ -892,9 +848,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -907,9 +861,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDkimFailConnectionsBySumId({ query, userKey: user._key, @@ -928,9 +880,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -959,11 +909,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec DKIM. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec DKIM. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -974,9 +920,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { }) describe('given a database error occurs', () => { it('returns an error message', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadDkimFailConnectionsBySumId({ query: mockedQuery, @@ -994,11 +938,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec DKIM. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec DKIM. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -1031,11 +971,7 @@ describe('given the loadDkimFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec DKIM. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec DKIM. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-failure-connections-by-sum-id.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-failure-connections-by-sum-id.test.js index a256ec36a..230f56230 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-failure-connections-by-sum-id.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-failure-connections-by-sum-id.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -12,15 +13,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the loadDmarcFailConnectionsBySumId loader', () => { - let query, - drop, - truncate, - collections, - i18n, - user, - dmarcSummary, - dmarcFailure1, - dmarcFailure2 + let query, drop, truncate, collections, i18n, user, dmarcSummary, dmarcFailure1, dmarcFailure2 const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -37,20 +30,19 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { describe('given a successful load', () => { beforeEach(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -448,9 +440,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { await connectionLoader({ ...connectionArgs }) } catch (err) { expect(err).toEqual( - new Error( - '`first` on the `DmarcFailureTable` connection cannot be less than zero.', - ), + new Error('`first` on the `DmarcFailureTable` connection cannot be less than zero.'), ) } @@ -476,11 +466,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { try { await connectionLoader({ ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `DmarcFailureTable` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `DmarcFailureTable` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -492,9 +478,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcFailConnectionsBySumId({ query, userKey: user._key, @@ -512,11 +496,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -528,9 +508,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcFailConnectionsBySumId({ query, userKey: user._key, @@ -548,11 +526,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -579,11 +553,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { try { await connectionLoader({ ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to load DMARC failure data. Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to load DMARC failure data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -594,9 +564,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { }) describe('given a database error', () => { it('returns an error message', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadDmarcFailConnectionsBySumId({ query: mockedQuery, @@ -614,9 +582,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DMARC failure data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DMARC failure data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -649,9 +615,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DMARC failure data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DMARC failure data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -813,9 +777,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { await connectionLoader({ ...connectionArgs }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro.', - ), + new Error('`first` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro.'), ) } @@ -842,9 +804,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { await connectionLoader({ ...connectionArgs }) } catch (err) { expect(err).toEqual( - new Error( - '`last` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro.', - ), + new Error('`last` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro.'), ) } @@ -857,9 +817,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcFailConnectionsBySumId({ query, userKey: user._key, @@ -878,9 +836,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -893,9 +849,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcFailConnectionsBySumId({ query, userKey: user._key, @@ -914,9 +868,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -944,11 +896,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { try { await connectionLoader({ ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec DMARC. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec DMARC. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -959,9 +907,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { }) describe('given a database error', () => { it('returns an error message', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadDmarcFailConnectionsBySumId({ query: mockedQuery, @@ -979,11 +925,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec DMARC. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec DMARC. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -1016,11 +958,7 @@ describe('given the loadDmarcFailConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec DMARC. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec DMARC. Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-connections-by-user-id.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-connections-by-user-id.test.js index 75e4cfb80..6fdcaca5f 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-connections-by-user-id.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-connections-by-user-id.test.js @@ -1,31 +1,19 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' -import { - loadDmarcSummaryConnectionsByUserId, - loadDmarcSummaryByKey, -} from '../index' +import { loadDmarcSummaryConnectionsByUserId, loadDmarcSummaryByKey } from '../index' import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the loadDmarcSummaryConnectionsByUserId function', () => { - let query, - drop, - truncate, - collections, - org, - i18n, - user, - domain1, - domain2, - dmarcSummary1, - dmarcSummary2 + let query, drop, truncate, collections, org, i18n, user, domain1, domain2, dmarcSummary1, dmarcSummary2 const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -43,22 +31,21 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -181,9 +168,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -208,10 +193,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -231,9 +213,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -258,10 +238,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -281,9 +258,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -307,10 +282,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -330,9 +302,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -356,10 +326,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -379,10 +346,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) it('returns the filtered dmarc summaries', async () => { const summaryLoader = loadDmarcSummaryByKey({ query }) - const expectedSummaries = await summaryLoader.loadMany([ - dmarcSummary1._key, - dmarcSummary2._key, - ]) + const expectedSummaries = await summaryLoader.loadMany([dmarcSummary1._key, dmarcSummary2._key]) const connectionLoader = loadDmarcSummaryConnectionsByUserId({ query, @@ -390,9 +354,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -417,10 +379,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -443,9 +402,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -464,10 +421,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -477,14 +431,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -503,9 +451,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -524,10 +470,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -537,14 +480,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -565,9 +502,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -586,10 +521,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -599,14 +531,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -625,9 +551,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -646,10 +570,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -659,14 +580,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -687,9 +602,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -708,10 +621,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -721,14 +631,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -747,9 +651,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -768,10 +670,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -781,14 +680,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -809,9 +702,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -830,10 +721,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -843,14 +731,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -869,9 +751,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -890,10 +770,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -903,14 +780,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -931,9 +802,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -952,10 +821,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -965,14 +831,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -991,9 +851,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1012,10 +870,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1025,14 +880,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1053,9 +902,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1074,10 +921,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -1087,14 +931,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -1113,9 +951,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1134,10 +970,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1147,14 +980,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1175,9 +1002,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1196,10 +1021,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -1209,14 +1031,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -1235,9 +1051,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1256,10 +1070,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1269,14 +1080,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1297,9 +1102,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1318,10 +1121,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -1331,14 +1131,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -1357,9 +1151,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1378,10 +1170,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1391,14 +1180,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1419,9 +1202,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1440,10 +1221,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -1453,14 +1231,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -1479,9 +1251,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1500,10 +1270,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1513,14 +1280,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1541,9 +1302,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1562,10 +1321,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -1575,14 +1331,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -1601,9 +1351,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1622,10 +1370,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1635,14 +1380,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1665,9 +1404,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1686,10 +1423,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1699,14 +1433,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1725,9 +1453,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1746,10 +1472,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -1759,14 +1482,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -1787,9 +1504,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1808,10 +1523,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1821,14 +1533,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1847,9 +1553,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1868,10 +1572,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -1881,14 +1582,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -1909,9 +1604,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1930,10 +1623,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -1943,14 +1633,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -1969,9 +1653,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -1990,10 +1672,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2003,14 +1682,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2031,9 +1704,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2052,10 +1723,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -2065,14 +1733,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -2091,9 +1753,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2112,10 +1772,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2125,14 +1782,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2153,9 +1804,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2174,10 +1823,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -2187,14 +1833,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -2213,9 +1853,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2234,10 +1872,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2247,14 +1882,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2275,9 +1904,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2296,10 +1923,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -2309,14 +1933,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -2335,9 +1953,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2356,10 +1972,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2369,14 +1982,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2397,9 +2004,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2418,10 +2023,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -2431,14 +2033,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -2457,9 +2053,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2478,10 +2072,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2491,14 +2082,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2519,9 +2104,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2540,10 +2123,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -2553,14 +2133,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -2579,9 +2153,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2600,10 +2172,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2613,14 +2182,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2641,9 +2204,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2662,10 +2223,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -2675,14 +2233,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -2701,9 +2253,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2722,10 +2272,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2735,14 +2282,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2763,9 +2304,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2784,10 +2323,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), node: { ...expectedSummaries[0], }, @@ -2797,14 +2333,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), }, } @@ -2823,9 +2353,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2844,10 +2372,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), node: { ...expectedSummaries[1], }, @@ -2857,14 +2382,8 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), - endCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[1]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2886,9 +2405,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n: {}, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -2919,10 +2436,58 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'dmarcSummary', - expectedSummaries[0]._key, - ), + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + }, + } + + expect(summaries).toEqual(expectedStructure) + }) + }) + describe('isAffiliated is set to true', () => { + it('returns dmarc summaries', async () => { + const expectedSummaries = await loadDmarcSummaryByKey({ + query, + }).loadMany([dmarcSummary1._key, dmarcSummary2._key]) + + const connectionLoader = loadDmarcSummaryConnectionsByUserId({ + query, + userKey: user._key, + cleanseInput, + auth: { loginRequired: true }, + i18n: {}, + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), + }) + + const connectionArgs = { + first: 10, + period: 'thirtyDays', + year: '2021', + isAffiliated: true, + } + + const summaries = await connectionLoader({ ...connectionArgs }) + + const expectedStructure = { + edges: [ + { + cursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), + node: { + ...expectedSummaries[0], + }, + }, + { + cursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), + node: { + ...expectedSummaries[1], + }, + }, + ], + totalCount: 2, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: toGlobalId('dmarcSummary', expectedSummaries[0]._key), endCursor: toGlobalId('dmarcSummary', expectedSummaries[1]._key), }, } @@ -2992,9 +2557,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3026,9 +2589,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3063,9 +2624,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3098,9 +2657,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3135,9 +2692,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3151,11 +2706,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `DmarcSummaries` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `DmarcSummaries` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadDmarcSummaryConnectionsByUserId.`, @@ -3170,9 +2721,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3186,11 +2735,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `DmarcSummaries` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `DmarcSummaries` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadDmarcSummaryConnectionsByUserId.`, @@ -3201,18 +2746,14 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcSummaryConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3226,11 +2767,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -3242,18 +2779,14 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcSummaryConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3267,11 +2800,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -3290,9 +2819,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3306,9 +2833,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'You must provide a `period` value to access the `DmarcSummaries` connection.', - ), + new Error('You must provide a `period` value to access the `DmarcSummaries` connection.'), ) } expect(consoleOutput).toEqual([ @@ -3324,9 +2849,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3340,9 +2863,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'You must provide a `year` value to access the `DmarcSummaries` connection.', - ), + new Error('You must provide a `year` value to access the `DmarcSummaries` connection.'), ) } expect(consoleOutput).toEqual([ @@ -3353,9 +2874,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { describe('given a database error', () => { describe('while querying for domain information', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadDmarcSummaryConnectionsByUserId({ query, @@ -3363,9 +2882,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3378,11 +2895,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to load DMARC summary data. Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to load DMARC summary data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -3407,9 +2920,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3422,11 +2933,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to load DMARC summary data. Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to load DMARC summary data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -3461,9 +2968,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3495,9 +3000,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3532,9 +3035,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3567,9 +3068,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3604,9 +3103,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3621,9 +3118,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro.', - ), + new Error('`first` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro.'), ) } expect(consoleOutput).toEqual([ @@ -3639,9 +3134,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3656,9 +3149,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`last` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro.', - ), + new Error('`last` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro.'), ) } expect(consoleOutput).toEqual([ @@ -3670,18 +3161,14 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcSummaryConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3696,9 +3183,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -3711,18 +3196,14 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDmarcSummaryConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3737,9 +3218,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -3759,9 +3238,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3775,9 +3252,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Vous devez fournir une valeur `period` pour accéder à la connexion `DmarcSummaries`.', - ), + new Error('Vous devez fournir une valeur `period` pour accéder à la connexion `DmarcSummaries`.'), ) } expect(consoleOutput).toEqual([ @@ -3793,9 +3268,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3809,9 +3282,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Vous devez fournir une valeur `year` pour accéder à la connexion `DmarcSummaries`.', - ), + new Error('Vous devez fournir une valeur `year` pour accéder à la connexion `DmarcSummaries`.'), ) } expect(consoleOutput).toEqual([ @@ -3822,9 +3293,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { describe('given a database error', () => { describe('while querying for domain information', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadDmarcSummaryConnectionsByUserId({ query, @@ -3832,9 +3301,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3848,9 +3315,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de charger les données de synthèse DMARC. Veuillez réessayer.', - ), + new Error('Impossible de charger les données de synthèse DMARC. Veuillez réessayer.'), ) } @@ -3876,9 +3341,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { cleanseInput, auth: { loginRequired: true }, i18n, - loadStartDateFromPeriod: jest - .fn() - .mockReturnValueOnce('thirtyDays'), + loadStartDateFromPeriod: jest.fn().mockReturnValueOnce('thirtyDays'), }) const connectionArgs = { @@ -3892,9 +3355,7 @@ describe('given the loadDmarcSummaryConnectionsByUserId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de charger les données de synthèse DMARC. Veuillez réessayer.', - ), + new Error('Impossible de charger les données de synthèse DMARC. Veuillez réessayer.'), ) } diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-edge-by-domain-id-period.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-edge-by-domain-id-period.test.js index c434fd36e..e0661ce69 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-edge-by-domain-id-period.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-sum-edge-by-domain-id-period.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -26,26 +27,25 @@ describe('given the loadDmarcSummaryEdgeByDomainIdAndPeriod loader', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) - + dmarcSummary = await collections.dmarcSummaries.save({ detailTables: { dkimFailure: [], @@ -111,27 +111,23 @@ describe('given the loadDmarcSummaryEdgeByDomainIdAndPeriod loader', () => { }) describe('given a database error', () => { it('throws an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred')) - + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred')) + const loader = loadDmarcSummaryEdgeByDomainIdAndPeriod({ query: mockedQuery, userKey: user._key, i18n, }) - + try { await loader({ domainId: 'domains/1', startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DMARC summary data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DMARC summary data. Please try again.')) } - + expect(consoleOutput).toEqual([ `Database error occurred when user: ${user._key} attempted to load dmarc summaries for domain: domains/1, period: thirtyDays, Error: Database error occurred`, ]) @@ -145,24 +141,22 @@ describe('given the loadDmarcSummaryEdgeByDomainIdAndPeriod loader', () => { }, } const mockedQuery = jest.fn().mockReturnValueOnce(cursor) - + const loader = loadDmarcSummaryEdgeByDomainIdAndPeriod({ query: mockedQuery, userKey: user._key, i18n, }) - + try { await loader({ domainId: 'domains/1', startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DMARC summary data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DMARC summary data. Please try again.')) } - + expect(consoleOutput).toEqual([ `Cursor error occurred when user: ${user._key} attempted to load dmarc summaries for domain: domains/1, period: thirtyDays, Error: Cursor error occurred.`, ]) @@ -186,29 +180,23 @@ describe('given the loadDmarcSummaryEdgeByDomainIdAndPeriod loader', () => { }) describe('given a database error', () => { it('throws an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred')) - + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred')) + const loader = loadDmarcSummaryEdgeByDomainIdAndPeriod({ query: mockedQuery, userKey: user._key, i18n, }) - + try { await loader({ domainId: 'domains/1', startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger les données de synthèse DMARC. Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger les données de synthèse DMARC. Veuillez réessayer.')) } - + expect(consoleOutput).toEqual([ `Database error occurred when user: ${user._key} attempted to load dmarc summaries for domain: domains/1, period: thirtyDays, Error: Database error occurred`, ]) @@ -222,26 +210,22 @@ describe('given the loadDmarcSummaryEdgeByDomainIdAndPeriod loader', () => { }, } const mockedQuery = jest.fn().mockReturnValueOnce(cursor) - + const loader = loadDmarcSummaryEdgeByDomainIdAndPeriod({ query: mockedQuery, userKey: user._key, i18n, }) - + try { await loader({ domainId: 'domains/1', startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger les données de synthèse DMARC. Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger les données de synthèse DMARC. Veuillez réessayer.')) } - + expect(consoleOutput).toEqual([ `Cursor error occurred when user: ${user._key} attempted to load dmarc summaries for domain: domains/1, period: thirtyDays, Error: Cursor error occurred.`, ]) diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-summary-by-key.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-summary-by-key.test.js index 026dd709c..33626679d 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-summary-by-key.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-dmarc-summary-by-key.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,14 +10,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the loadDmarcSummaryByKey dataloader', () => { - let query, - drop, - truncate, - collections, - i18n, - domain, - dmarcSummary1, - dmarcSummary2 + let query, drop, truncate, collections, i18n, domain, dmarcSummary1, dmarcSummary2 const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -30,16 +24,16 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) beforeEach(async () => { domain = await collections.domains.save({ @@ -108,13 +102,13 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { it('returns a single dmarc summary', async () => { const expectedCursor = await query` FOR summary IN dmarcSummaries - SORT summary._key ASC + SORT summary._key ASC LIMIT 1 LET edge = ( FOR v, e IN 1..1 ANY summary._id domainsToDmarcSummaries RETURN e ) - + RETURN { _id: summary._id, _key: summary._key, @@ -131,9 +125,7 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { const expectedSummary = await expectedCursor.next() expectedSummary.domainKey = domain._key - const summary = await loadDmarcSummaryByKey({ query }).load( - expectedSummary._key, - ) + const summary = await loadDmarcSummaryByKey({ query }).load(expectedSummary._key) expect(summary).toEqual(expectedSummary) }) @@ -148,7 +140,7 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { FOR v, e IN 1..1 ANY summary._id domainsToDmarcSummaries RETURN e ) - + RETURN { _id: summary._id, _key: summary._key, @@ -169,9 +161,7 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { expectedSummaries.push(temp) } - const summaries = await loadDmarcSummaryByKey({ query }).loadMany( - summaryKeys, - ) + const summaries = await loadDmarcSummaryByKey({ query }).loadMany(summaryKeys) expect(summaries).toEqual(expectedSummaries) }) @@ -194,18 +184,12 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { }) describe('database error occurs', () => { it('throws an error', async () => { - query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) try { - await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load( - '1234', - ) + await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load('1234') } catch (err) { - expect(err).toEqual( - new Error('Unable to find DMARC summary data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to find DMARC summary data. Please try again.')) } expect(consoleOutput).toEqual([ `Database error occurred when user: 1234 running loadDmarcSummaryByKey: Error: Database error occurred.`, @@ -222,13 +206,9 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { query = jest.fn().mockReturnValue(cursor) try { - await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load( - '1234', - ) + await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load('1234') } catch (err) { - expect(err).toEqual( - new Error('Unable to find DMARC summary data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to find DMARC summary data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -254,20 +234,12 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { }) describe('database error occurs', () => { it('throws an error', async () => { - query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) try { - await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load( - '1234', - ) + await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load('1234') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de trouver les données de synthèse DMARC. Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de trouver les données de synthèse DMARC. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ `Database error occurred when user: 1234 running loadDmarcSummaryByKey: Error: Database error occurred.`, @@ -284,15 +256,9 @@ describe('given the loadDmarcSummaryByKey dataloader', () => { query = jest.fn().mockReturnValue(cursor) try { - await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load( - '1234', - ) + await loadDmarcSummaryByKey({ query, userKey: '1234', i18n }).load('1234') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de trouver les données de synthèse DMARC. Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de trouver les données de synthèse DMARC. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-full-pass-connections-by-sum-id.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-full-pass-connections-by-sum-id.test.js index a445adf05..16bc47873 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-full-pass-connections-by-sum-id.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-full-pass-connections-by-sum-id.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -12,15 +13,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the loadFullPassConnectionsBySumId loader', () => { - let query, - drop, - truncate, - collections, - i18n, - user, - dmarcSummary, - fullPass1, - fullPass2 + let query, drop, truncate, collections, i18n, user, dmarcSummary, fullPass1, fullPass2 const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -37,26 +30,25 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) - + fullPass1 = { sourceIpAddress: '123.456.78.91', envelopeFrom: 'envelope.from', @@ -68,7 +60,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { dnsHost: 'dns.host.ca', id: 1, } - + fullPass2 = { sourceIpAddress: '123.456.78.91', envelopeFrom: 'envelope.from', @@ -80,7 +72,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { dnsHost: 'dns.host.ca', id: 2, } - + dmarcSummary = await collections.dmarcSummaries.save({ detailTables: { dkimFailure: [], @@ -116,15 +108,15 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 100, after: toGlobalId('fullPass', 1), summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -143,7 +135,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { endCursor: toGlobalId('fullPass', 2), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -155,15 +147,15 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 100, before: toGlobalId('fullPass', 2), summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -182,7 +174,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { endCursor: toGlobalId('fullPass', 1), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -194,14 +186,14 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 1, summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -220,7 +212,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { endCursor: toGlobalId('fullPass', 1), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -232,14 +224,14 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 1, summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -258,7 +250,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { endCursor: toGlobalId('fullPass', 2), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -266,21 +258,21 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { describe('given there are no full passes to load', () => { it('returns no full pass connections', async () => { await truncate() - + const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { last: 1, summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [], totalCount: 0, @@ -291,7 +283,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { endCursor: '', }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -321,7 +313,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { summaryId: '', } @@ -336,7 +328,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} did not have either \`first\` or \`last\` arguments set for: loadFullPassConnectionsBySumId.`, ]) @@ -350,7 +342,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 1, last: 1, @@ -367,7 +359,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` and \`last\` arguments set for: loadFullPassConnectionsBySumId.`, ]) @@ -382,7 +374,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 101, summaryId: '', @@ -398,7 +390,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set to 101 for: loadFullPassConnectionsBySumId.`, ]) @@ -412,7 +404,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 101, summaryId: '', @@ -428,7 +420,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set to 101 for: loadFullPassConnectionsBySumId.`, ]) @@ -444,7 +436,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: -1, summaryId: '', @@ -454,13 +446,9 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `FullPassTable` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `FullPassTable` connection cannot be less than zero.')) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadFullPassConnectionsBySumId.`, ]) @@ -474,7 +462,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: -1, summaryId: '', @@ -484,13 +472,9 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `FullPassTable` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `FullPassTable` connection cannot be less than zero.')) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadFullPassConnectionsBySumId.`, ]) @@ -500,31 +484,25 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -536,31 +514,25 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { last: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -579,7 +551,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 1, } @@ -588,11 +560,9 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load full pass data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load full pass data. Please try again.')) } - + expect(consoleOutput).toEqual([ `SummaryId was undefined when user: ${user._key} attempted to load full passes in loadFullPassConnectionsBySumId.`, ]) @@ -601,17 +571,15 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) describe('given a database error occurs', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) - + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -621,11 +589,9 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load full pass data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load full pass data. Please try again.')) } - + expect(consoleOutput).toEqual([ `Database error occurred while user: ${user._key} was trying to gather full passes in loadFullPassConnectionsBySumId, error: Error: Database error occurred.`, ]) @@ -639,14 +605,14 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }, } const query = jest.fn().mockReturnValueOnce(cursor) - + const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -656,11 +622,9 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load full pass data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load full pass data. Please try again.')) } - + expect(consoleOutput).toEqual([ `Cursor error occurred while user: ${user._key} was trying to gather full passes in loadFullPassConnectionsBySumId, error: Error: Cursor error occurred.`, ]) @@ -691,7 +655,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { summaryId: '', } @@ -706,7 +670,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} did not have either \`first\` or \`last\` arguments set for: loadFullPassConnectionsBySumId.`, ]) @@ -720,7 +684,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 1, last: 1, @@ -737,7 +701,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` and \`last\` arguments set for: loadFullPassConnectionsBySumId.`, ]) @@ -752,7 +716,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 101, summaryId: '', @@ -768,7 +732,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set to 101 for: loadFullPassConnectionsBySumId.`, ]) @@ -782,7 +746,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 101, summaryId: '', @@ -798,7 +762,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set to 101 for: loadFullPassConnectionsBySumId.`, ]) @@ -814,7 +778,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: -1, summaryId: '', @@ -825,12 +789,10 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `FullPassTable` ne peut être inférieur à zéro.', - ), + new Error('`first` sur la connexion `FullPassTable` ne peut être inférieur à zéro.'), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadFullPassConnectionsBySumId.`, ]) @@ -844,7 +806,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: -1, summaryId: '', @@ -854,13 +816,9 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` sur la connexion `FullPassTable` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`last` sur la connexion `FullPassTable` ne peut être inférieur à zéro.')) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadFullPassConnectionsBySumId.`, ]) @@ -870,30 +828,26 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -906,30 +860,26 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { last: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -949,7 +899,7 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 1, } @@ -959,12 +909,10 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de charger les données complètes de la passe. Veuillez réessayer.', - ), + new Error('Impossible de charger les données complètes de la passe. Veuillez réessayer.'), ) } - + expect(consoleOutput).toEqual([ `SummaryId was undefined when user: ${user._key} attempted to load full passes in loadFullPassConnectionsBySumId.`, ]) @@ -973,17 +921,15 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) describe('given a database error occurs', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) - + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -994,12 +940,10 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de charger les données complètes de la passe. Veuillez réessayer.', - ), + new Error('Impossible de charger les données complètes de la passe. Veuillez réessayer.'), ) } - + expect(consoleOutput).toEqual([ `Database error occurred while user: ${user._key} was trying to gather full passes in loadFullPassConnectionsBySumId, error: Error: Database error occurred.`, ]) @@ -1013,14 +957,14 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }, } const query = jest.fn().mockReturnValueOnce(cursor) - + const connectionLoader = loadFullPassConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -1031,12 +975,10 @@ describe('given the loadFullPassConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de charger les données complètes de la passe. Veuillez réessayer.', - ), + new Error('Impossible de charger les données complètes de la passe. Veuillez réessayer.'), ) } - + expect(consoleOutput).toEqual([ `Cursor error occurred while user: ${user._key} was trying to gather full passes in loadFullPassConnectionsBySumId, error: Error: Cursor error occurred.`, ]) diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-spf-failure-connections-by-sum-id.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-spf-failure-connections-by-sum-id.test.js index 0461ca720..a781d5f94 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-spf-failure-connections-by-sum-id.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-spf-failure-connections-by-sum-id.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -12,15 +13,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the loadSpfFailureConnectionsBySumId loader', () => { - let query, - drop, - truncate, - collections, - i18n, - user, - dmarcSummary, - spfFailure1, - spfFailure2 + let query, drop, truncate, collections, i18n, user, dmarcSummary, spfFailure1, spfFailure2 const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -37,26 +30,25 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) - + spfFailure1 = { sourceIpAddress: '123.456.78.91', envelopeFrom: 'envelope.from', @@ -69,7 +61,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { dnsHost: 'dns.host.ca', guidance: '', } - + spfFailure2 = { sourceIpAddress: '123.456.78.91', envelopeFrom: 'envelope.from', @@ -82,7 +74,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { dnsHost: 'dns.host.ca', guidance: '', } - + dmarcSummary = await collections.dmarcSummaries.save({ detailTables: { dkimFailure: [], @@ -118,15 +110,15 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 100, after: toGlobalId('spfFail', 1), summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -145,7 +137,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { endCursor: toGlobalId('spfFail', 2), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -157,15 +149,15 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 100, before: toGlobalId('spfFail', 2), summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -184,7 +176,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { endCursor: toGlobalId('spfFail', 1), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -196,14 +188,14 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 1, summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -222,7 +214,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { endCursor: toGlobalId('spfFail', 1), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -234,14 +226,14 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 1, summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [ { @@ -260,7 +252,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { endCursor: toGlobalId('spfFail', 2), }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -268,21 +260,21 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { describe('given there are no spf failures to load', () => { it('returns no spf failure connections', async () => { await truncate() - + const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 1, summaryId: dmarcSummary._id, } - + const summaries = await connectionLoader({ ...connectionArgs }) - + const expectedStructure = { edges: [], totalCount: 0, @@ -293,7 +285,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { endCursor: '', }, } - + expect(summaries).toEqual(expectedStructure) }) }) @@ -323,7 +315,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { summaryId: '', } @@ -338,7 +330,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} did not have either \`first\` or \`last\` arguments set for: loadSpfFailureConnectionsBySumId.`, ]) @@ -352,7 +344,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 1, last: 1, @@ -369,7 +361,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` and \`last\` arguments set for: loadSpfFailureConnectionsBySumId.`, ]) @@ -384,7 +376,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 101, summaryId: '', @@ -400,7 +392,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set to 101 for: loadSpfFailureConnectionsBySumId.`, ]) @@ -414,7 +406,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 101, summaryId: '', @@ -430,7 +422,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set to 101 for: loadSpfFailureConnectionsBySumId.`, ]) @@ -446,7 +438,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: -1, summaryId: '', @@ -456,13 +448,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `SpfFailureTable` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `SpfFailureTable` connection cannot be less than zero.')) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadSpfFailureConnectionsBySumId.`, ]) @@ -476,7 +464,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: -1, summaryId: '', @@ -486,13 +474,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `SpfFailureTable` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `SpfFailureTable` connection cannot be less than zero.')) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadSpfFailureConnectionsBySumId.`, ]) @@ -502,31 +486,25 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -538,31 +516,25 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { last: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -581,7 +553,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 10, } @@ -590,11 +562,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load SPF failure data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load SPF failure data. Please try again.')) } - + expect(consoleOutput).toEqual([ `SummaryId was undefined when user: ${user._key} attempted to load spf failures in loadSpfFailureConnectionsBySumId.`, ]) @@ -603,17 +573,15 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }) describe('given a database error', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) - + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -623,11 +591,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load SPF failure data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load SPF failure data. Please try again.')) } - + expect(consoleOutput).toEqual([ `Database error occurred while user: ${user._key} was trying to gather spf failures in loadSpfFailureConnectionsBySumId, error: Error: Database error occurred.`, ]) @@ -641,14 +607,14 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }, } const query = jest.fn().mockReturnValueOnce(cursor) - + const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -658,11 +624,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load SPF failure data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load SPF failure data. Please try again.')) } - + expect(consoleOutput).toEqual([ `Cursor error occurred while user: ${user._key} was trying to gather spf failures in loadSpfFailureConnectionsBySumId, error: Error: Cursor error occurred.`, ]) @@ -693,7 +657,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { summaryId: '', } @@ -708,7 +672,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} did not have either \`first\` or \`last\` arguments set for: loadSpfFailureConnectionsBySumId.`, ]) @@ -722,7 +686,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 1, last: 1, @@ -739,7 +703,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` and \`last\` arguments set for: loadSpfFailureConnectionsBySumId.`, ]) @@ -754,7 +718,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 101, summaryId: '', @@ -770,7 +734,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set to 101 for: loadSpfFailureConnectionsBySumId.`, ]) @@ -784,7 +748,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: 101, summaryId: '', @@ -800,7 +764,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set to 101 for: loadSpfFailureConnectionsBySumId.`, ]) @@ -816,7 +780,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: -1, summaryId: '', @@ -827,12 +791,10 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro.', - ), + new Error('`first` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro.'), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`first\` set below zero for: loadSpfFailureConnectionsBySumId.`, ]) @@ -846,7 +808,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { last: -1, summaryId: '', @@ -857,12 +819,10 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`last` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro.', - ), + new Error('`last` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro.'), ) } - + expect(consoleOutput).toEqual([ `User: ${user._key} attempted to have \`last\` set below zero for: loadSpfFailureConnectionsBySumId.`, ]) @@ -872,30 +832,26 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { describe('first or last argument is not set to a number', () => { describe('first argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -908,30 +864,26 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }) describe('last argument is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { last: invalidInput, summaryId: '', } - + try { await connectionLoader({ ...connectionArgs, }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -951,7 +903,7 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { cleanseInput, i18n, }) - + const connectionArgs = { first: 10, } @@ -960,13 +912,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec SPF. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec SPF. Veuillez réessayer.")) } - + expect(consoleOutput).toEqual([ `SummaryId was undefined when user: ${user._key} attempted to load spf failures in loadSpfFailureConnectionsBySumId.`, ]) @@ -975,17 +923,15 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }) describe('given a database error', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) - + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -995,13 +941,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec SPF. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec SPF. Veuillez réessayer.")) } - + expect(consoleOutput).toEqual([ `Database error occurred while user: ${user._key} was trying to gather spf failures in loadSpfFailureConnectionsBySumId, error: Error: Database error occurred.`, ]) @@ -1015,14 +957,14 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { }, } const query = jest.fn().mockReturnValueOnce(cursor) - + const connectionLoader = loadSpfFailureConnectionsBySumId({ query, userKey: user._key, cleanseInput, i18n, }) - + const connectionArgs = { first: 50, summaryId: '', @@ -1032,13 +974,9 @@ describe('given the loadSpfFailureConnectionsBySumId loader', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger les données d'échec SPF. Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger les données d'échec SPF. Veuillez réessayer.")) } - + expect(consoleOutput).toEqual([ `Cursor error occurred while user: ${user._key} was trying to gather spf failures in loadSpfFailureConnectionsBySumId, error: Error: Cursor error occurred.`, ]) diff --git a/api/src/dmarc-summaries/loaders/__tests__/load-yearly-dmarc-sum-edges.test.js b/api/src/dmarc-summaries/loaders/__tests__/load-yearly-dmarc-sum-edges.test.js index 31c0c7d6a..38bd63605 100644 --- a/api/src/dmarc-summaries/loaders/__tests__/load-yearly-dmarc-sum-edges.test.js +++ b/api/src/dmarc-summaries/loaders/__tests__/load-yearly-dmarc-sum-edges.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,15 +10,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the loadDmarcYearlySumEdge loader', () => { - let query, - drop, - truncate, - collections, - i18n, - user, - dmarcSummary1, - dmarcSummary2, - dmarcSummary3 + let query, drop, truncate, collections, i18n, user, dmarcSummary1, dmarcSummary2, dmarcSummary3 const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -35,22 +28,21 @@ describe('given the loadDmarcYearlySumEdge loader', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -154,9 +146,7 @@ describe('given the loadDmarcYearlySumEdge loader', () => { }) describe('given a database error', () => { it('throws an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred')) const loader = loadDmarcYearlySumEdge({ query: mockedQuery, @@ -170,9 +160,7 @@ describe('given the loadDmarcYearlySumEdge loader', () => { startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DMARC summary data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DMARC summary data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -201,9 +189,7 @@ describe('given the loadDmarcYearlySumEdge loader', () => { startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load DMARC summary data. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load DMARC summary data. Please try again.')) } expect(consoleOutput).toEqual([ @@ -229,9 +215,7 @@ describe('given the loadDmarcYearlySumEdge loader', () => { }) describe('given a database error', () => { it('throws an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred')) const loader = loadDmarcYearlySumEdge({ query: mockedQuery, @@ -245,11 +229,7 @@ describe('given the loadDmarcYearlySumEdge loader', () => { startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger les données de synthèse DMARC. Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger les données de synthèse DMARC. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -278,11 +258,7 @@ describe('given the loadDmarcYearlySumEdge loader', () => { startDate: 'thirtyDays', }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger les données de synthèse DMARC. Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger les données de synthèse DMARC. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/dmarc-summaries/loaders/index.js b/api/src/dmarc-summaries/loaders/index.js index a8a6b12b1..c5cb74548 100644 --- a/api/src/dmarc-summaries/loaders/index.js +++ b/api/src/dmarc-summaries/loaders/index.js @@ -7,3 +7,4 @@ export * from './load-full-pass-connections-by-sum-id' export * from './load-spf-failure-connections-by-sum-id' export * from './load-start-date-from-period' export * from './load-yearly-dmarc-sum-edges' +export * from './load-all-verified-rua-domains' diff --git a/api/src/dmarc-summaries/loaders/load-all-verified-rua-domains.js b/api/src/dmarc-summaries/loaders/load-all-verified-rua-domains.js new file mode 100644 index 000000000..7ac89a22c --- /dev/null +++ b/api/src/dmarc-summaries/loaders/load-all-verified-rua-domains.js @@ -0,0 +1,31 @@ +import { t } from '@lingui/macro' + +export const loadAllVerifiedRuaDomains = + ({ query, userKey, i18n }) => + async () => { + let verifiedRuaDomains + try { + verifiedRuaDomains = ( + await query` + FOR org IN organizations + FILTER org.verified == true + SORT org.orgDetails.en.acronym ASC + LET domains = ( + FOR domain,claim IN 1..1 OUTBOUND org claims + FILTER domain.archived != true + FILTER domain.ignoreRua != true + FILTER domain.rcode != "NXDOMAIN" + FILTER domain.hasCyberRua == true + SORT domain.domain ASC + RETURN domain.domain + ) + RETURN { key: CONCAT(org.orgDetails.en.acronym, "-", org.orgDetails.fr.acronym), domains } + ` + ).all() + } catch (err) { + console.error(`Database error occurred when user: ${userKey} was trying to load verified rua domains: ${err}`) + throw new Error(i18n._(t`Unable to load verified rua domains. Please try again.`)) + } + + return verifiedRuaDomains + } diff --git a/api/src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js b/api/src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js index cab6a06a7..941985cf6 100644 --- a/api/src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js +++ b/api/src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js @@ -4,7 +4,7 @@ import { t } from '@lingui/macro' export const loadDmarcSummaryConnectionsByUserId = ({ query, userKey, cleanseInput, i18n, loadStartDateFromPeriod }) => - async ({ after, before, first, last, period, year, orderBy, isSuperAdmin, search }) => { + async ({ after, before, first, last, period, year, orderBy, isSuperAdmin, search, isAffiliated }) => { const userDBId = `users/${userKey}` if (typeof period === 'undefined') { @@ -316,36 +316,41 @@ export const loadDmarcSummaryConnectionsByUserId = sortString = aql`ASC` } - let domainQuery = aql`` let searchDomainFilter = aql`` - if (typeof search !== 'undefined') { + if (typeof search !== 'undefined' && search !== '') { search = cleanseInput(search) - domainQuery = aql` - LET tokenArr = TOKENS(${search}, "space-delimiter-analyzer") - LET searchedDomains = ( - FOR token IN tokenArr - FOR domain IN domainSearch - SEARCH ANALYZER(domain.domain LIKE CONCAT("%", token, "%"), "space-delimiter-analyzer") - RETURN domain._id - ) - ` - searchDomainFilter = aql`FILTER domainId IN searchedDomains` + searchDomainFilter = aql`FILTER userDomain.domain LIKE LOWER(CONCAT("%", ${search}, "%"))` } - let domainIdQueries + let domainQueries if (isSuperAdmin) { - domainIdQueries = aql` + domainQueries = aql` WITH affiliations, dmarcSummaries, domains, domainsToDmarcSummaries, organizations, ownership, users, domainSearch - LET domainIds = UNIQUE(FLATTEN( + LET userDomains = UNIQUE(FLATTEN( LET ids = [] LET orgIds = (FOR org IN organizations RETURN org._id) FOR orgId IN orgIds - LET claimDomainIds = (FOR v, e IN 1..1 OUTBOUND orgId ownership RETURN v._id) - RETURN APPEND(ids, claimDomainIds) + LET claimUserDomains = (FOR v, e IN 1..1 OUTBOUND orgId ownership RETURN v) + RETURN APPEND(ids, claimUserDomains) )) ` + } else if (isAffiliated) { + domainQueries = aql` + WITH affiliations, dmarcSummaries, domains, domainsToDmarcSummaries, organizations, ownership, users, domainSearch + LET userAffiliations = ( + FOR v, e IN 1..1 ANY ${userDBId} affiliations + FILTER e.permission != "pending" + RETURN v + ) + LET userDomains = UNIQUE( + FOR org IN organizations + FILTER org._key IN userAffiliations[*]._key + FOR v, e IN 1..1 OUTBOUND org._id ownership + RETURN v + ) + ` } else { - domainIdQueries = aql` + domainQueries = aql` WITH affiliations, dmarcSummaries, domains, domainsToDmarcSummaries, organizations, ownership, users, domainSearch LET userAffiliations = ( FOR v, e IN 1..1 ANY ${userDBId} affiliations @@ -353,11 +358,11 @@ export const loadDmarcSummaryConnectionsByUserId = RETURN v ) LET hasVerifiedOrgAffiliation = POSITION(userAffiliations[*].verified, true) - LET domainIds = UNIQUE( + LET userDomains = UNIQUE( FOR org IN organizations FILTER org._key IN userAffiliations[*]._key || (hasVerifiedOrgAffiliation == true && org.verified == true) FOR v, e IN 1..1 OUTBOUND org._id ownership - RETURN v._id + RETURN v ) ` } @@ -365,17 +370,15 @@ export const loadDmarcSummaryConnectionsByUserId = let requestedSummaryInfo try { requestedSummaryInfo = await query` - ${domainIdQueries} - - ${domainQuery} + ${domainQueries} ${afterVar} ${beforeVar} LET summaryIds = ( - FOR domainId IN domainIds + FOR userDomain IN userDomains ${searchDomainFilter} - FOR v, e IN 1..1 ANY domainId domainsToDmarcSummaries + FOR v, e IN 1..1 ANY userDomain._id domainsToDmarcSummaries FILTER e.startDate == ${startDate} RETURN e._to ) diff --git a/api/src/dmarc-summaries/objects/__tests__/dkim-failure-table.test.js b/api/src/dmarc-summaries/objects/__tests__/dkim-failure-table.test.js index 34448ba32..133de70e0 100644 --- a/api/src/dmarc-summaries/objects/__tests__/dkim-failure-table.test.js +++ b/api/src/dmarc-summaries/objects/__tests__/dkim-failure-table.test.js @@ -142,7 +142,7 @@ describe('given the dkimFailureTable gql object', () => { const demoType = dkimFailureTableType.getFields() const expectedResults = { - _id: 'aggregateGuidanceTags/agg1', + _id: 'guidanceTags/agg1', _key: 'agg1', _rev: 'rev', _type: 'guidanceTag', @@ -164,10 +164,8 @@ describe('given the dkimFailureTable gql object', () => { { guidance: 'agg1' }, {}, { - loaders: { - loadAggregateGuidanceTagByTagId: { - load: jest.fn().mockReturnValue(expectedResults), - }, + dataSources: { + guidanceTag: { byTagId: jest.fn().mockReturnValue([expectedResults]) }, }, }, ), @@ -185,10 +183,8 @@ describe('given the dkimFailureTable gql object', () => { { guidance: null }, {}, { - loaders: { - loadAggregateGuidanceTagByTagId: { - load: jest.fn().mockReturnValue(expectedResults), - }, + dataSources: { + guidanceTag: { byTagId: jest.fn().mockReturnValue(expectedResults) }, }, }, ), diff --git a/api/src/dmarc-summaries/objects/__tests__/dmarc-summary.test.js b/api/src/dmarc-summaries/objects/__tests__/dmarc-summary.test.js index ed0b50fcb..16a781d3a 100644 --- a/api/src/dmarc-summaries/objects/__tests__/dmarc-summary.test.js +++ b/api/src/dmarc-summaries/objects/__tests__/dmarc-summary.test.js @@ -79,8 +79,8 @@ describe('testing the period gql object', () => { { domainKey: domain._key }, {}, { - loaders: { - loadDomainByKey: { load: jest.fn().mockReturnValue(domain) }, + dataSources: { + domain: { byKey: { load: jest.fn().mockReturnValue(domain) } }, }, }, ), diff --git a/api/src/dmarc-summaries/objects/__tests__/spf-failure-table.test.js b/api/src/dmarc-summaries/objects/__tests__/spf-failure-table.test.js index 7a26fe867..c35d341b5 100644 --- a/api/src/dmarc-summaries/objects/__tests__/spf-failure-table.test.js +++ b/api/src/dmarc-summaries/objects/__tests__/spf-failure-table.test.js @@ -108,7 +108,7 @@ describe('given spfFailureTable gql object', () => { const demoType = spfFailureTableType.getFields() const expectedResult = { - _id: 'aggregateGuidanceTags/agg1', + _id: 'guidanceTags/agg1', _key: 'agg1', _rev: 'rev', _type: 'guidanceTag', @@ -130,10 +130,8 @@ describe('given spfFailureTable gql object', () => { { guidance: 'agg1' }, {}, { - loaders: { - loadAggregateGuidanceTagByTagId: { - load: jest.fn().mockReturnValue(expectedResult), - }, + dataSources: { + guidanceTag: { byTagId: jest.fn().mockReturnValue([expectedResult]) }, }, }, ), @@ -151,10 +149,8 @@ describe('given spfFailureTable gql object', () => { { guidance: null }, {}, { - loaders: { - loadAggregateGuidanceTagByTagId: { - load: jest.fn().mockReturnValue(expectedResult), - }, + dataSources: { + guidanceTag: { byTagId: jest.fn().mockReturnValue(expectedResult) }, }, }, ), diff --git a/api/src/dmarc-summaries/objects/dkim-failure-table.js b/api/src/dmarc-summaries/objects/dkim-failure-table.js index 950f31243..140022b47 100644 --- a/api/src/dmarc-summaries/objects/dkim-failure-table.js +++ b/api/src/dmarc-summaries/objects/dkim-failure-table.js @@ -1,17 +1,11 @@ -import { - GraphQLInt, - GraphQLObjectType, - GraphQLString, - GraphQLBoolean, -} from 'graphql' +import { GraphQLInt, GraphQLObjectType, GraphQLString, GraphQLBoolean } from 'graphql' import { globalIdField } from 'graphql-relay' import { guidanceTagType } from '../../guidance-tag/objects' export const dkimFailureTableType = new GraphQLObjectType({ name: 'DkimFailureTable', - description: - 'This table contains the data fields for senders who are in the DKIM fail category.', + description: 'This table contains the data fields for senders who are in the DKIM fail category.', fields: () => ({ id: globalIdField('dkimFail'), dkimAligned: { @@ -55,13 +49,10 @@ export const dkimFailureTableType = new GraphQLObjectType({ guidanceTag: { type: guidanceTagType, description: 'Guidance for any issues that were found from the report.', - resolve: async ( - { guidance }, - _args, - { loaders: { loadAggregateGuidanceTagByTagId } }, - ) => { + resolve: async ({ guidance }, _args, { dataSources: { guidanceTag } }) => { if (guidance) { - return await loadAggregateGuidanceTagByTagId.load(guidance) + const guidanceTags = await guidanceTag.byTagId({ tags: [guidance] }) + return guidanceTags[0] } return {} }, diff --git a/api/src/dmarc-summaries/objects/dmarc-summary.js b/api/src/dmarc-summaries/objects/dmarc-summary.js index 571fd1ccf..2da1e6231 100644 --- a/api/src/dmarc-summaries/objects/dmarc-summary.js +++ b/api/src/dmarc-summaries/objects/dmarc-summary.js @@ -20,9 +20,9 @@ export const dmarcSummaryType = new GraphQLObjectType({ resolve: async ( { domainKey }, _args, - { loaders: { loadDomainByKey } }, + { dataSources: { domain: domainDataSource } }, ) => { - const domain = await loadDomainByKey.load(domainKey) + const domain = await domainDataSource.byKey.load(domainKey) return domain }, }, diff --git a/api/src/dmarc-summaries/objects/spf-failure-table.js b/api/src/dmarc-summaries/objects/spf-failure-table.js index 19e1e34cd..f15cddf52 100644 --- a/api/src/dmarc-summaries/objects/spf-failure-table.js +++ b/api/src/dmarc-summaries/objects/spf-failure-table.js @@ -1,17 +1,11 @@ -import { - GraphQLInt, - GraphQLObjectType, - GraphQLString, - GraphQLBoolean, -} from 'graphql' +import { GraphQLInt, GraphQLObjectType, GraphQLString, GraphQLBoolean } from 'graphql' import { globalIdField } from 'graphql-relay' import { guidanceTagType } from '../../guidance-tag/objects' export const spfFailureTableType = new GraphQLObjectType({ name: 'SpfFailureTable', - description: - 'This table contains the data fields for senders who are in the SPF fail category.', + description: 'This table contains the data fields for senders who are in the SPF fail category.', fields: () => ({ id: globalIdField('spfFail'), dnsHost: { @@ -34,13 +28,10 @@ export const spfFailureTableType = new GraphQLObjectType({ guidanceTag: { type: guidanceTagType, description: 'Guidance for any issues that were found from the report.', - resolve: async ( - { guidance }, - _args, - { loaders: { loadAggregateGuidanceTagByTagId } }, - ) => { + resolve: async ({ guidance }, _args, { dataSources: { guidanceTag } }) => { if (guidance) { - return await loadAggregateGuidanceTagByTagId.load(guidance) + const guidanceTags = await guidanceTag.byTagId({ tags: [guidance] }) + return guidanceTags[0] } return {} }, diff --git a/api/src/dmarc-summaries/queries/__tests__/find-my-dmarc-summaries.test.js b/api/src/dmarc-summaries/queries/__tests__/find-my-dmarc-summaries.test.js index 232ad38df..9c837bbe8 100644 --- a/api/src/dmarc-summaries/queries/__tests__/find-my-dmarc-summaries.test.js +++ b/api/src/dmarc-summaries/queries/__tests__/find-my-dmarc-summaries.test.js @@ -1,5 +1,6 @@ import moment from 'moment' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -72,7 +73,6 @@ describe('given the findMyDmarcSummaries query', () => { user = await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: true, }) diff --git a/api/src/dmarc-summaries/queries/__tests__/get-all-verified-rua-domains.test.js b/api/src/dmarc-summaries/queries/__tests__/get-all-verified-rua-domains.test.js new file mode 100644 index 000000000..6633df066 --- /dev/null +++ b/api/src/dmarc-summaries/queries/__tests__/get-all-verified-rua-domains.test.js @@ -0,0 +1,35 @@ +import { getAllVerifiedRuaDomains } from '../get-all-verified-rua-domains' + +describe('getAllVerifiedRuaDomains', () => { + it('should return a JSON string', async () => { + const mockUserRequired = jest.fn().mockResolvedValue({ id: 'testUser' }) + const mockVerifiedRequired = jest.fn() + const mockCheckSuperAdmin = jest.fn().mockResolvedValue(true) + const mockSuperAdminRequired = jest.fn() + const mockLoadAllVerifiedRuaDomains = jest.fn().mockResolvedValue([{ key: 'testKey', domains: 'testDomains' }]) + + const result = await getAllVerifiedRuaDomains.resolve( + {}, + {}, + { + userKey: 'testUserKey', + auth: { + checkSuperAdmin: mockCheckSuperAdmin, + userRequired: mockUserRequired, + verifiedRequired: mockVerifiedRequired, + superAdminRequired: mockSuperAdminRequired, + }, + loaders: { + loadAllVerifiedRuaDomains: mockLoadAllVerifiedRuaDomains, + }, + }, + ) + + expect(result).toBe(JSON.stringify({ testKey: 'testDomains' }, null, 4)) + expect(mockUserRequired).toHaveBeenCalled() + expect(mockVerifiedRequired).toHaveBeenCalledWith({ user: { id: 'testUser' } }) + expect(mockCheckSuperAdmin).toHaveBeenCalled() + expect(mockSuperAdminRequired).toHaveBeenCalledWith({ user: { id: 'testUser' }, isSuperAdmin: true }) + expect(mockLoadAllVerifiedRuaDomains).toHaveBeenCalledWith({}) + }) +}) diff --git a/api/src/dmarc-summaries/queries/find-my-dmarc-summaries.js b/api/src/dmarc-summaries/queries/find-my-dmarc-summaries.js index 165f62c35..cda2e995a 100644 --- a/api/src/dmarc-summaries/queries/find-my-dmarc-summaries.js +++ b/api/src/dmarc-summaries/queries/find-my-dmarc-summaries.js @@ -1,4 +1,4 @@ -import { GraphQLNonNull, GraphQLString } from 'graphql' +import { GraphQLBoolean, GraphQLNonNull, GraphQLString } from 'graphql' import { connectionArgs } from 'graphql-relay' import { dmarcSummaryOrder } from '../inputs' @@ -26,6 +26,10 @@ export const findMyDmarcSummaries = { type: GraphQLString, description: 'An optional string used to filter the results based on domains.', }, + isAffiliated: { + type: GraphQLBoolean, + description: 'Filter the results based on the users affiliation.', + }, ...connectionArgs, }, resolve: async ( diff --git a/api/src/dmarc-summaries/queries/get-all-verified-rua-domains.js b/api/src/dmarc-summaries/queries/get-all-verified-rua-domains.js new file mode 100644 index 000000000..1107d43c8 --- /dev/null +++ b/api/src/dmarc-summaries/queries/get-all-verified-rua-domains.js @@ -0,0 +1,32 @@ +import { GraphQLString } from 'graphql' + +export const getAllVerifiedRuaDomains = { + type: GraphQLString, + description: 'JSON formatted output of all domains in verified organizations that send DMARC reports.', + resolve: async ( + _, + args, + { + userKey, + auth: { checkSuperAdmin, userRequired, verifiedRequired, superAdminRequired }, + loaders: { loadAllVerifiedRuaDomains }, + }, + ) => { + const user = await userRequired() + verifiedRequired({ user }) + + const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ user, isSuperAdmin }) + + const ruaDomains = await loadAllVerifiedRuaDomains({ ...args }) + + console.info(`User ${userKey} successfully retrieved all domains with DMARC reports.`) + + const returnObj = {} + ruaDomains.forEach(({ key, domains }) => { + returnObj[key] = domains + }) + + return JSON.stringify(returnObj, null, 4) + }, +} diff --git a/api/src/dmarc-summaries/queries/index.js b/api/src/dmarc-summaries/queries/index.js index 94bbe8747..790f25d21 100644 --- a/api/src/dmarc-summaries/queries/index.js +++ b/api/src/dmarc-summaries/queries/index.js @@ -1 +1,2 @@ export * from './find-my-dmarc-summaries' +export * from './get-all-verified-rua-domains' diff --git a/api/src/dns-scan/data-source.js b/api/src/dns-scan/data-source.js new file mode 100644 index 000000000..b7d232121 --- /dev/null +++ b/api/src/dns-scan/data-source.js @@ -0,0 +1,8 @@ +import { loadDnsByKey, loadDnsConnectionsByDomainId } from './loaders' + +export class DnsScanDataSource { + constructor({ query, userKey, cleanseInput, i18n }) { + this.byKey = loadDnsByKey({ query, userKey, i18n }) + this.getConnectionsByDomainId = loadDnsConnectionsByDomainId({ query, userKey, cleanseInput, i18n }) + } +} diff --git a/api/src/dns-scan/index.js b/api/src/dns-scan/index.js index 6d2a5b587..52de3be3a 100644 --- a/api/src/dns-scan/index.js +++ b/api/src/dns-scan/index.js @@ -1,3 +1,4 @@ +export * from './data-source' export * from './inputs' export * from './loaders' export * from './objects' diff --git a/api/src/dns-scan/loaders/index.js b/api/src/dns-scan/loaders/index.js index 6d093db6d..9b63653ab 100644 --- a/api/src/dns-scan/loaders/index.js +++ b/api/src/dns-scan/loaders/index.js @@ -1,3 +1,2 @@ export * from './load-dns-by-key' export * from './load-dns-connections-by-domain-id' -export * from './load-mx-record-diff-by-domain-id' diff --git a/api/src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js b/api/src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js deleted file mode 100644 index a84eeb956..000000000 --- a/api/src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js +++ /dev/null @@ -1,241 +0,0 @@ -import { aql } from 'arangojs' -import { t } from '@lingui/macro' - -export const loadMxRecordDiffByDomainId = - ({ query, userKey, cleanseInput, i18n }) => - async ({ limit, domainId, startDate, endDate, after, before, offset, orderBy }) => { - if (limit === undefined) { - console.warn(`User: ${userKey} did not set \`limit\` argument for: loadMxRecordDiffByDomainId.`) - throw new Error(i18n._(t`You must provide a \`limit\` value to properly paginate the \`MXRecord\` connection.`)) - } - - if (limit <= 0 || limit > 100) { - console.warn(`User: ${userKey} set \`limit\` argument outside accepted range: loadMxRecordDiffByDomainId.`) - throw new Error( - i18n._( - t`You must provide a \`limit\` value in the range of 1-100 to properly paginate the \`MXRecord\` connection.`, - ), - ) - } - - const paginationMethodCount = [before, after, offset].reduce( - (paginationMethod, currentValue) => currentValue + (paginationMethod === undefined), - 0, - ) - - if (paginationMethodCount > 1) { - console.warn(`User: ${userKey} set multiple pagination methods for: loadMxRecordDiffByDomainId.`) - throw new Error( - i18n._( - t`You must provide at most one pagination method (\`before\`, \`after\`, \`offset\`) value to properly paginate the \`MXRecord\` connection.`, - ), - ) - } - - before = cleanseInput(before) - after = cleanseInput(after) - - const usingRelayExplicitly = !!(before || after) - - const resolveCursor = (cursor) => { - const cursorString = Buffer.from(cursor, 'base64').toString('utf8').split('|') - - return cursorString.reduce((acc, currentValue) => { - const [type, id] = currentValue.split('::') - acc.push({ type, id }) - return acc - }, []) - } - let relayBeforeTemplate = aql`` - let relayAfterTemplate = aql`` - if (usingRelayExplicitly) { - const cursorList = resolveCursor(after || before) - - if (cursorList.length === 0 || cursorList > 2) { - // TODO: throw error - } - - if (cursorList.at(-1).type !== 'id') { - // id field should always be last property - // TODO: throw error - } - - const orderByDirectionArrow = - orderBy?.direction === 'DESC' ? aql`<` : orderBy?.direction === 'ASC' ? aql`>` : null - const reverseOrderByDirectionArrow = - orderBy?.direction === 'DESC' ? aql`>` : orderBy?.direction === 'ASC' ? aql`<` : null - - relayBeforeTemplate = aql`FILTER TO_NUMBER(dnsScan._key) < TO_NUMBER(${cursorList[0].id})` - relayAfterTemplate = aql`FILTER TO_NUMBER(dnsScan._key) > TO_NUMBER(${cursorList[0].id})` - - if (cursorList.length === 2) { - relayAfterTemplate = aql` - FILTER dnsScan.${cursorList[0].type} ${orderByDirectionArrow || aql`>`} ${cursorList[0].id} - OR (dnsScan.${cursorList[0].type} == ${cursorList[0].id} - AND TO_NUMBER(dnsScan._key) > TO_NUMBER(${cursorList[1].id})) - ` - - relayBeforeTemplate = aql` - FILTER dnsScan.${cursorList[0].type} ${reverseOrderByDirectionArrow || aql`<`} ${cursorList[0].id} - OR (dnsScan.${cursorList[0].type} == ${cursorList[0].id} - AND TO_NUMBER(dnsScan._key) < TO_NUMBER(${cursorList[1].id})) - ` - } - } - - const relayDirectionString = before ? aql`DESC` : aql`ASC` - - let sortTemplate - if (!orderBy) { - sortTemplate = aql`SORT TO_NUMBER(dnsScan._key) ${relayDirectionString}` - } else { - sortTemplate = aql`SORT dnsScan.${orderBy.field} ${orderBy.direction}, TO_NUMBER(dnsScan._key) ${relayDirectionString}` - } - - let startDateFilter = aql`` - if (typeof startDate !== 'undefined') { - startDateFilter = aql` - FILTER DATE_FORMAT(dnsScan.timestamp, '%yyyy-%mm-%dd') >= DATE_FORMAT(${startDate}, '%yyyy-%mm-%dd')` - } - - let endDateFilter = aql`` - if (typeof endDate !== 'undefined') { - endDateFilter = aql` - FILTER DATE_FORMAT(dnsScan.timestamp, '%yyyy-%mm-%dd') <= DATE_FORMAT(${endDate}, '%yyyy-%mm-%dd')` - } - - const removeExtraSliceTemplate = aql`SLICE(dnsScansPlusOne, 0, ${limit})` - const dnsScanQuery = aql` - WITH dns, domains - LET dnsScansPlusOne = ( - FOR dnsScan, e IN 1 OUTBOUND ${domainId} domainsDNS - FILTER dnsScan.mxRecords.diff == true - ${startDateFilter} - ${endDateFilter} - ${before ? relayBeforeTemplate : relayAfterTemplate} - ${sortTemplate} - LIMIT ${limit + 1} - RETURN { id: dnsScan._key, _type: "dnsScan", timestamp: dnsScan.timestamp, mxRecords: dnsScan.mxRecords } - ) - LET hasMoreRelayPage = LENGTH(dnsScansPlusOne) == ${limit} + 1 - LET hasReversePage = ${!usingRelayExplicitly} ? false : (LENGTH( - FOR dnsScan, e IN 1 OUTBOUND ${domainId} domainsDNS - FILTER dnsScan.mxRecords.diff == true - ${startDateFilter} - ${endDateFilter} - ${before ? relayAfterTemplate : relayBeforeTemplate} - LIMIT 1 - RETURN true - ) > 0) ? true : false - LET totalCount = COUNT( - FOR dnsScan, e IN 1 OUTBOUND ${domainId} domainsDNS - FILTER dnsScan.mxRecords.diff == true - ${startDateFilter} - ${endDateFilter} - RETURN true - ) - LET mxRecords = ${removeExtraSliceTemplate} - - RETURN { - "mxRecords": mxRecords, - "hasMoreRelayPage": hasMoreRelayPage, - "hasReversePage": hasReversePage, - "totalCount": totalCount - } - ` - - let mxRecordCursor - try { - mxRecordCursor = await query`${dnsScanQuery}` - } catch (err) { - console.error( - `Database error occurred while user: ${userKey} was trying to get cursor for DNS document with cursor '${ - after || before - }' for domain '${domainId}', error: ${err}`, - ) - throw new Error(i18n._(t`Unable to load DNS scan(s). Please try again.`)) - } - - let mxRecordInfo - try { - mxRecordInfo = await mxRecordCursor.next() - } catch (err) { - console.error( - `Cursor error occurred while user: ${userKey} was trying to get DNS information for ${domainId}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to load DNS scan(s). Please try again.`)) - } - - const mxRecords = mxRecordInfo.mxRecords - - if (mxRecords.length === 0) { - return { - edges: [], - totalCount: mxRecordInfo.totalCount, - pageInfo: { - hasPreviousPage: !usingRelayExplicitly - ? false - : after - ? mxRecordInfo.hasReversePage - : mxRecordInfo.hasMoreRelayPage, - hasNextPage: after || !usingRelayExplicitly ? mxRecordInfo.hasMoreRelayPage : mxRecordInfo.hasReversePage, - startCursor: null, - endCursor: null, - }, - } - } - - const toCursorString = (cursorObjects) => { - const cursorStringArray = cursorObjects.reduce((acc, cursorObject) => { - if (cursorObject.type === undefined || cursorObject.id === undefined) { - // TODO: throw error - } - acc.push(`${cursorObject.type}::${cursorObject.id}`) - return acc - }, []) - const cursorString = cursorStringArray.join('|') - return Buffer.from(cursorString, 'utf8').toString('base64') - } - - const edges = mxRecords.map((mxRecord) => { - let cursor - if (orderBy) { - cursor = toCursorString([ - { - type: orderBy.field, - id: mxRecord[orderBy.field], - }, - { - type: 'id', - id: mxRecord._key, - }, - ]) - } else { - cursor = toCursorString([ - { - type: 'id', - id: mxRecord._key, - }, - ]) - } - return { - cursor: cursor, - node: mxRecord, - } - }) - - return { - edges: edges, - totalCount: mxRecordInfo.totalCount, - pageInfo: { - hasPreviousPage: !usingRelayExplicitly - ? false - : after - ? mxRecordInfo.hasReversePage - : mxRecordInfo.hasMoreRelayPage, - hasNextPage: after || !usingRelayExplicitly ? mxRecordInfo.hasMoreRelayPage : mxRecordInfo.hasReversePage, - endCursor: edges.length > 0 ? edges.at(-1).cursor : null, - startCursor: edges.length > 0 ? edges[0].cursor : null, - }, - } - } diff --git a/api/src/dns-scan/objects/dkim-selector-result.js b/api/src/dns-scan/objects/dkim-selector-result.js index 48ca2cd31..0a51b0a47 100644 --- a/api/src/dns-scan/objects/dkim-selector-result.js +++ b/api/src/dns-scan/objects/dkim-selector-result.js @@ -42,22 +42,22 @@ export const dkimSelectorResultType = new GraphQLObjectType({ positiveTags: { type: new GraphQLList(guidanceTagType), description: `List of positive tags for the scanned domain from this scan.`, - resolve: async ({ positiveTags }, _, { loaders: { loadDkimGuidanceTagByTagId } }) => { - return await loadDkimGuidanceTagByTagId({ tags: positiveTags }) + resolve: async ({ positiveTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.dkimByTagId({ tags: positiveTags }) }, }, neutralTags: { type: new GraphQLList(guidanceTagType), description: `List of neutral tags for the scanned domain from this scan.`, - resolve: async ({ neutralTags }, _, { loaders: { loadDkimGuidanceTagByTagId } }) => { - return await loadDkimGuidanceTagByTagId({ tags: neutralTags }) + resolve: async ({ neutralTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.dkimByTagId({ tags: neutralTags }) }, }, negativeTags: { type: new GraphQLList(guidanceTagType), description: `List of negative tags for the scanned domain from this scan.`, - resolve: async ({ negativeTags }, _, { loaders: { loadDkimGuidanceTagByTagId } }) => { - return await loadDkimGuidanceTagByTagId({ tags: negativeTags }) + resolve: async ({ negativeTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.dkimByTagId({ tags: negativeTags }) }, }, }), diff --git a/api/src/dns-scan/objects/dkim.js b/api/src/dns-scan/objects/dkim.js index 682b6079a..ed5ab22d1 100644 --- a/api/src/dns-scan/objects/dkim.js +++ b/api/src/dns-scan/objects/dkim.js @@ -13,22 +13,22 @@ export const dkimType = new GraphQLObjectType({ positiveTags: { type: new GraphQLList(guidanceTagType), description: `List of positive tags for the scanned domain from this scan.`, - resolve: async ({ positiveTags }, _, { loaders: { loadDkimGuidanceTagByTagId } }) => { - return await loadDkimGuidanceTagByTagId({ tags: positiveTags }) + resolve: async ({ positiveTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: positiveTags }) }, }, neutralTags: { type: new GraphQLList(guidanceTagType), description: `List of neutral tags for the scanned domain from this scan.`, - resolve: async ({ neutralTags }, _, { loaders: { loadDkimGuidanceTagByTagId } }) => { - return await loadDkimGuidanceTagByTagId({ tags: neutralTags }) + resolve: async ({ neutralTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: neutralTags }) }, }, negativeTags: { type: new GraphQLList(guidanceTagType), description: `List of negative tags for the scanned domain from this scan.`, - resolve: async ({ negativeTags }, _, { loaders: { loadDkimGuidanceTagByTagId } }) => { - return await loadDkimGuidanceTagByTagId({ tags: negativeTags }) + resolve: async ({ negativeTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: negativeTags }) }, }, selectors: { diff --git a/api/src/dns-scan/objects/dmarc.js b/api/src/dns-scan/objects/dmarc.js index f907ef51f..dcaf40eb5 100644 --- a/api/src/dns-scan/objects/dmarc.js +++ b/api/src/dns-scan/objects/dmarc.js @@ -39,22 +39,22 @@ subdomains where mail is failing the DMARC authentication and alignment checks.` positiveTags: { type: new GraphQLList(guidanceTagType), description: `List of positive tags for the scanned domain from this scan.`, - resolve: async ({ positiveTags }, _, { loaders: { loadDmarcGuidanceTagByTagId } }) => { - return await loadDmarcGuidanceTagByTagId({ tags: positiveTags }) + resolve: async ({ positiveTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: positiveTags }) }, }, neutralTags: { type: new GraphQLList(guidanceTagType), description: `List of neutral tags for the scanned domain from this scan.`, - resolve: async ({ neutralTags }, _, { loaders: { loadDmarcGuidanceTagByTagId } }) => { - return await loadDmarcGuidanceTagByTagId({ tags: neutralTags }) + resolve: async ({ neutralTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: neutralTags }) }, }, negativeTags: { type: new GraphQLList(guidanceTagType), description: `List of negative tags for the scanned domain from this scan.`, - resolve: async ({ negativeTags }, _, { loaders: { loadDmarcGuidanceTagByTagId } }) => { - return await loadDmarcGuidanceTagByTagId({ tags: negativeTags }) + resolve: async ({ negativeTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: negativeTags }) }, }, }), diff --git a/api/src/dns-scan/objects/dns-scan.js b/api/src/dns-scan/objects/dns-scan.js index 7da6d4bb2..898666d7f 100644 --- a/api/src/dns-scan/objects/dns-scan.js +++ b/api/src/dns-scan/objects/dns-scan.js @@ -74,5 +74,9 @@ export const nsRecordType = new GraphQLObjectType({ type: new GraphQLList(GraphQLString), description: `Additional warning info about the NS record.`, }, + error: { + type: GraphQLString, + description: `Error message if the NS record could not be retrieved.`, + }, }), }) diff --git a/api/src/dns-scan/objects/index.js b/api/src/dns-scan/objects/index.js index 130811182..3ff308445 100644 --- a/api/src/dns-scan/objects/index.js +++ b/api/src/dns-scan/objects/index.js @@ -3,6 +3,5 @@ export * from './dkim-selector-result' export * from './dmarc' export * from './dns-scan' export * from './dns-scan-connection' -export * from './mx-record-connection' export * from './mx-record' export * from './spf' diff --git a/api/src/dns-scan/objects/mx-record-connection.js b/api/src/dns-scan/objects/mx-record-connection.js deleted file mode 100644 index f33a7e314..000000000 --- a/api/src/dns-scan/objects/mx-record-connection.js +++ /dev/null @@ -1,16 +0,0 @@ -import { GraphQLInt } from 'graphql' -import { connectionDefinitions } from 'graphql-relay' - -import { mxRecordDiffType } from './mx-record' - -export const mxRecordConnection = connectionDefinitions({ - name: 'MXRecordDiff', - nodeType: mxRecordDiffType, - connectionFields: () => ({ - totalCount: { - type: GraphQLInt, - description: 'The total amount of DNS scans related to a given domain.', - resolve: ({ totalCount }) => totalCount, - }, - }), -}) diff --git a/api/src/dns-scan/objects/mx-record.js b/api/src/dns-scan/objects/mx-record.js index 49a71fbea..f4a339f4f 100644 --- a/api/src/dns-scan/objects/mx-record.js +++ b/api/src/dns-scan/objects/mx-record.js @@ -1,6 +1,4 @@ import { GraphQLInt, GraphQLList, GraphQLObjectType, GraphQLString } from 'graphql' -import { GraphQLDateTime } from 'graphql-scalars' -import { globalIdField } from 'graphql-relay' export const mxHostType = new GraphQLObjectType({ name: 'MXHost', @@ -32,22 +30,9 @@ export const mxRecordType = new GraphQLObjectType({ type: new GraphQLList(GraphQLString), description: `Additional warning info about the MX record.`, }, - }), -}) - -export const mxRecordDiffType = new GraphQLObjectType({ - name: 'MXRecordDiff', - fields: () => ({ - id: globalIdField('dns'), - timestamp: { - type: GraphQLDateTime, - description: `The time when the scan was initiated.`, - resolve: ({ timestamp }) => new Date(timestamp), - }, - mxRecords: { - type: mxRecordType, - description: `The MX records for the domain (if they exist).`, - resolve: ({ mxRecords }) => mxRecords, + error: { + type: GraphQLString, + description: `Error message if the MX record could not be retrieved.`, }, }), }) diff --git a/api/src/dns-scan/objects/spf.js b/api/src/dns-scan/objects/spf.js index 27f697aeb..9104d7aa1 100644 --- a/api/src/dns-scan/objects/spf.js +++ b/api/src/dns-scan/objects/spf.js @@ -27,22 +27,22 @@ export const spfType = new GraphQLObjectType({ positiveTags: { type: new GraphQLList(guidanceTagType), description: `List of positive tags for the scanned domain from this scan.`, - resolve: async ({ positiveTags }, _, { loaders: { loadSpfGuidanceTagByTagId } }) => { - return await loadSpfGuidanceTagByTagId({ tags: positiveTags }) + resolve: async ({ positiveTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: positiveTags }) }, }, neutralTags: { type: new GraphQLList(guidanceTagType), description: `List of neutral tags for the scanned domain from this scan.`, - resolve: async ({ neutralTags }, _, { loaders: { loadSpfGuidanceTagByTagId } }) => { - return await loadSpfGuidanceTagByTagId({ tags: neutralTags }) + resolve: async ({ neutralTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: neutralTags }) }, }, negativeTags: { type: new GraphQLList(guidanceTagType), description: `List of negative tags for the scanned domain from this scan.`, - resolve: async ({ negativeTags }, _, { loaders: { loadSpfGuidanceTagByTagId } }) => { - return await loadSpfGuidanceTagByTagId({ tags: negativeTags }) + resolve: async ({ negativeTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: negativeTags }) }, }, }), diff --git a/api/src/domain/data-source.js b/api/src/domain/data-source.js new file mode 100644 index 000000000..b569d1cac --- /dev/null +++ b/api/src/domain/data-source.js @@ -0,0 +1,887 @@ +import { t } from '@lingui/macro' +import { aql } from 'arangojs' + +import { + loadDomainByDomain, + loadDomainByKey, + loadDomainConnectionsByOrgId, + loadDomainConnectionsByUserId, + loadDkimSelectorsByDomainId, +} from './loaders' + +export class DomainDataSource { + constructor({ query, userKey, i18n, language, cleanseInput, loginRequiredBool, transaction, collections }) { + this._query = query + this._userKey = userKey + this._i18n = i18n + this._transaction = transaction + this._collections = collections + this.byDomain = loadDomainByDomain({ query, userKey, i18n }) + this.byKey = loadDomainByKey({ query, userKey, i18n }) + this.connectionsByOrgId = loadDomainConnectionsByOrgId({ + query, + userKey, + language, + cleanseInput, + i18n, + auth: { loginRequiredBool }, + }) + this.connectionsByUserId = loadDomainConnectionsByUserId({ + query, + userKey, + cleanseInput, + i18n, + auth: { loginRequiredBool }, + }) + this.dkimSelectorsByDomainId = loadDkimSelectorsByDomainId({ query, userKey, i18n }) + } + + async create({ insertDomain, org, tags, assetState }) { + const trx = await this._transaction(this._collections) + + let domainCursor + try { + domainCursor = await trx.step( + () => this._query` + UPSERT { domain: ${insertDomain.domain} } + INSERT ${insertDomain} + UPDATE { } + IN domains + RETURN NEW + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when inserting new domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + let insertedDomain + try { + insertedDomain = await domainCursor.next() + } catch (err) { + console.error(`Cursor error occurred for user: ${this._userKey} when inserting new domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH claims + INSERT { + _from: ${org._id}, + _to: ${insertedDomain._id}, + tags: ${tags}, + assetState: ${assetState}, + firstSeen: ${new Date().toISOString()}, + } INTO claims + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when inserting new domain edge: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was creating domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + this.byDomain.clear(insertDomain.domain) + return this.byDomain.load(insertDomain.domain) + } + + async favourite({ domain, user }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH favourites + INSERT { + _from: ${user._id}, + _to: ${domain._id}, + } INTO favourites + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when inserting new domain edge: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to favourite domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was creating domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to favourite domain. Please try again.`)) + } + } + + async isFavouritedByUser({ domainId, userId }) { + let checkDomainCursor + try { + checkDomainCursor = await this._query` + WITH domains + FOR v, e IN 1..1 ANY ${domainId} favourites + FILTER e._from == ${userId} + RETURN e + ` + } catch (err) { + console.error(`Database error occurred while running check to see if domain already favourited: ${err}`) + throw new Error(this._i18n._(t`Unable to favourite domain. Please try again.`)) + } + + let checkUserDomain + try { + checkUserDomain = await checkDomainCursor.next() + } catch (err) { + console.error(`Cursor error occurred while running check to see if domain already favourited: ${err}`) + throw new Error(this._i18n._(t`Unable to favourite domain. Please try again.`)) + } + + return typeof checkUserDomain !== 'undefined' + } + + async organizationsClaimingDomain({ domainId, domainName }) { + let countCursor + try { + countCursor = await this._query` + WITH claims, domains, organizations + FOR v, e IN 1..1 ANY ${domainId} claims + RETURN v + ` + } catch (err) { + console.error( + `Database error occurred for user: ${this._userKey}, when counting domain claims for domain: ${domainName || domainId}, error: ${err}`, + ) + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + return { + organizations: await countCursor.all(), + count: countCursor.count, + } + } + + async hasOwnershipClaim({ orgId, domainId, domainName }) { + let dmarcCountCursor + try { + dmarcCountCursor = await this._query` + WITH domains, organizations, ownership + FOR v IN 1..1 OUTBOUND ${orgId} ownership + FILTER v._id == ${domainId} + RETURN true + ` + } catch (err) { + console.error( + `Database error occurred for user: ${this._userKey}, when counting ownership claims for domain: ${domainName}, error: ${err}`, + ) + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + return dmarcCountCursor.count === 1 + } + + async organizationAlreadyClaimsDomainName({ orgId, domainName }) { + let checkDomainCursor + try { + checkDomainCursor = await this._query` + WITH claims, domains, organizations + LET domainIds = (FOR domain IN domains FILTER domain.domain == ${domainName} RETURN { id: domain._id }) + FOR domainId IN domainIds + LET domainEdges = (FOR v, e IN 1..1 ANY domainId.id claims RETURN { _from: e._from }) + FOR domainEdge IN domainEdges + LET org = DOCUMENT(domainEdge._from) + FILTER org._id == ${orgId} + RETURN true + ` + } catch (err) { + console.error(`Database error occurred while running check to see if domain already exists in an org: ${err}`) + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + let checkOrgDomain + try { + checkOrgDomain = await checkDomainCursor.next() + } catch (err) { + console.error(`Cursor error occurred while running check to see if domain already exists in an org: ${err}`) + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + return typeof checkOrgDomain !== 'undefined' + } + + async organizationHasClaim({ orgId, domainId, domainKey }) { + let countCursor + try { + countCursor = await this._query` + WITH claims, domains, organizations + FOR v, e IN 1..1 ANY ${domainId} claims + FILTER e._from == ${orgId} + RETURN e + ` + } catch (err) { + console.error( + `Database error occurred while user: ${this._userKey} attempted to update domain: ${domainKey}, error: ${err}`, + ) + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + + return countCursor.count > 0 + } + + async loadClaimByOrgAndDomain({ orgId, domainId }) { + let claimCursor + try { + claimCursor = await this._query` + WITH claims + FOR claim IN claims + FILTER claim._from == ${orgId} && claim._to == ${domainId} + RETURN MERGE({ id: claim._key, _type: "claim" }, claim) + ` + } catch (err) { + console.error(`Database error occurred when user: ${this._userKey} running loadDomainByKey: ${err}`) + return undefined + } + + if (typeof claimCursor?.next !== 'function') { + return undefined + } + + try { + return await claimCursor.next() + } catch (err) { + console.error(`Cursor error occurred when user: ${this._userKey} running loadDomainByKey: ${err}`) + return undefined + } + } + + async loadClaimForOrgByDomainKey({ orgId, domainKey }) { + let checkClaimCursor + try { + checkClaimCursor = await this._query` + WITH claims, domains, organizations + FOR v, e IN 1..1 ANY ${orgId} claims + FILTER v._key == ${domainKey} + RETURN { claim: e, domain: v.domain } + ` + } catch (err) { + console.error(`Database error occurred while running check to see if domain already exists in an org: ${err}`) + return undefined + } + + try { + return await checkClaimCursor.next() + } catch (err) { + console.error(`Cursor error occurred while running check to see if domain already exists in an org: ${err}`) + return undefined + } + } + + async loadClaimsForOrgByFilters({ orgId, filters, search }) { + let domainFilters = aql`` + if (typeof filters !== 'undefined') { + filters.forEach(({ filterCategory, comparison, filterValue }) => { + if (comparison === '==') { + comparison = aql`==` + } else { + comparison = aql`!=` + } + if (filterCategory === 'dmarc-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.dmarc ${comparison} ${filterValue} + ` + } else if (filterCategory === 'dkim-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.dkim ${comparison} ${filterValue} + ` + } else if (filterCategory === 'https-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.https ${comparison} ${filterValue} + ` + } else if (filterCategory === 'spf-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.spf ${comparison} ${filterValue} + ` + } else if (filterCategory === 'ciphers-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.ciphers ${comparison} ${filterValue} + ` + } else if (filterCategory === 'curves-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.curves ${comparison} ${filterValue} + ` + } else if (filterCategory === 'hsts-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.hsts ${comparison} ${filterValue} + ` + } else if (filterCategory === 'policy-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.policy ${comparison} ${filterValue} + ` + } else if (filterCategory === 'protocols-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.protocols ${comparison} ${filterValue} + ` + } else if (filterCategory === 'certificates-status') { + domainFilters = aql` + ${domainFilters} + FILTER v.status.certificates ${comparison} ${filterValue} + ` + } else if (filterCategory === 'tags') { + if (filterValue === 'archived') { + domainFilters = aql` + ${domainFilters} + FILTER v.archived ${comparison} true + ` + } else if (filterValue === 'nxdomain') { + domainFilters = aql` + ${domainFilters} + FILTER v.rcode ${comparison} "NXDOMAIN" + ` + } else if (filterValue === 'blocked') { + domainFilters = aql` + ${domainFilters} + FILTER v.blocked ${comparison} true + ` + } else if (filterValue === 'wildcard-sibling') { + domainFilters = aql` + ${domainFilters} + FILTER v.wildcardSibling ${comparison} true + ` + } else if (filterValue === 'wildcard-entry') { + domainFilters = aql` + ${domainFilters} + FILTER v.wildcardEntry ${comparison} true + ` + } else if (filterValue === 'scan-pending') { + domainFilters = aql` + ${domainFilters} + FILTER v.webScanPending ${comparison} true + ` + } else if (filterValue === 'has-entrust-certificate') { + domainFilters = aql` + ${domainFilters} + FILTER v.hasEntrustCertificate ${comparison} true + ` + } else if (filterValue === 'cve-detected') { + domainFilters = aql` + ${domainFilters} + FILTER v.cveDetected ${comparison} true + ` + } else { + domainFilters = aql` + ${domainFilters} + FILTER POSITION( e.tags, ${filterValue}) ${comparison} true + ` + } + } else if (filterCategory === 'asset-state') { + domainFilters = aql` + ${domainFilters} + FILTER e.assetState ${comparison} ${filterValue} + ` + } else if (filterCategory === 'guidance-tag') { + domainFilters = aql` + ${domainFilters} + FILTER POSITION(negativeTags, ${filterValue}) ${comparison} true + ` + } + }) + } + + let searchString = aql`` + if (typeof search !== 'undefined' && search !== '') { + searchString = aql`FILTER LOWER(v.domain) LIKE LOWER(${search})` + } + + let checkClaimsCursor + try { + checkClaimsCursor = await this._query` + WITH claims, domains, organizations + FOR v, e IN 1..1 ANY ${orgId} claims + ${domainFilters} + ${searchString} + RETURN { claim: e, domain: v.domain } + ` + } catch (err) { + console.error(`Database error occurred while running check to see if domain already exists in an org: ${err}`) + throw new Error(this._i18n._(t`Unable to update domains. Please try again.`)) + } + + let checkClaims + try { + checkClaims = await checkClaimsCursor.all() + } catch (err) { + console.error(`Cursor error occurred while running check to see if domain already exists in an org: ${err}`) + throw new Error(this._i18n._(t`Unable to update domains. Please try again.`)) + } + + if (typeof checkClaims === 'undefined') { + throw new Error(this._i18n._(t`Unable to update domains. Please try again.`)) + } + + return checkClaims + } + + async unfavourite({ domain, user }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH favourites, domains, users + LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} favourites RETURN { _key: e._key, _from: e._from, _to: e._to }) + LET edgeKeys = ( + FOR domainEdge IN domainEdges + FILTER domainEdge._to == ${domain._id} + FILTER domainEdge._from == ${user._id} + RETURN domainEdge._key + ) + FOR edgeKey IN edgeKeys + REMOVE edgeKey IN favourites + OPTIONS { waitForSync: true } + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when removing domain edge: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to unfavourite domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was unfavouriting domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to unfavourite domain. Please try again.`)) + } + } + + async update({ domain, org, domainToInsert, claimToInsert }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + WITH domains + UPSERT { _key: ${domain._key} } + INSERT ${domainToInsert} + UPDATE ${domainToInsert} + IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: ${this._userKey} attempted to update domain: ${domain._key}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + + try { + await trx.step( + async () => + await this._query` + WITH claims + UPSERT { _from: ${org._id}, _to: ${domain._id} } + INSERT ${claimToInsert} + UPDATE ${claimToInsert} + IN claims + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: ${this._userKey} attempted to update domain edge, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain edge. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Transaction commit error occurred when user: ${this._userKey} attempted to update domain: ${domain._key}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + + this.byKey.clear(domain._key) + return this.byKey.load(domain._key) + } + + async updateClaim({ claim, claimToInsert }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + WITH claims + UPSERT { _key: ${claim._key} } + INSERT ${claimToInsert} + UPDATE ${claimToInsert} + IN claims + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: ${this._userKey} attempted to update domain edge, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was updating domain claim: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + } + + async remove({ domain, org, orgsClaimingDomain, hasOwnership }) { + const trx = await this._transaction(this._collections) + + if (hasOwnership) { + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries + LET dmarcSummaryEdges = ( + FOR v, e IN 1..1 OUTBOUND ${domain._id} domainsToDmarcSummaries + RETURN { edgeKey: e._key, dmarcSummaryId: e._to } + ) + LET removeDmarcSummaryEdges = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries + OPTIONS { waitForSync: true } + ) + LET removeDmarcSummary = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key + REMOVE key IN dmarcSummaries + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error( + `Trx step error occurred when removing dmarc summary data for user: ${this._userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains + LET domainEdges = ( + FOR v, e IN 1..1 INBOUND ${domain._id} ownership + REMOVE e._key IN ownership + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error( + `Trx step error occurred when removing ownership data for user: ${this._userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } + + if (orgsClaimingDomain <= 1) { + try { + await trx.step(async () => { + await this._query` + WITH web, webScan, domains + FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb + LET removeWebScansQuery = ( + FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans + REMOVE webScanV IN webScan + REMOVE webToWebScansV IN webToWebScans + OPTIONS { waitForSync: true } + ) + REMOVE webV IN web + REMOVE domainsWebEdge IN domainsWeb + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove web data for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH dns, domains + FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS + REMOVE dnsV IN dns + REMOVE domainsDNSEdge IN domainsDNS + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove DNS data for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH favourites, domains + FOR fav IN favourites + FILTER fav._to == ${domain._id} + REMOVE fav IN favourites + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove favourites for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + FOR e IN domainsToSelectors + FILTER e._from == ${domain._id} + REMOVE e IN domainsToSelectors + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove DKIM selectors for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + FOR claim IN claims + FILTER claim._to == ${domain._id} + REMOVE claim IN claims + REMOVE ${domain} IN domains + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove domain ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } else { + try { + await trx.step(async () => { + await this._query` + WITH claims, domains, organizations + LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} claims RETURN { _key: e._key, _from: e._from, _to: e._to }) + LET edgeKeys = ( + FOR domainEdge IN domainEdges + FILTER domainEdge._to == ${domain._id} + FILTER domainEdge._from == ${org._id} + RETURN domainEdge._key + ) + FOR edgeKey IN edgeKeys + REMOVE edgeKey IN claims + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove claim for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Trx commit error occurred while user: ${this._userKey} attempted to remove ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } + + async ignoreCve({ domain, ignoredCve, newIgnoredCves }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + UPSERT { _key: ${domain._key} } + INSERT ${{ ignoredCves: newIgnoredCves }} + UPDATE ${{ ignoredCves: newIgnoredCves }} + IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + let currentDomainVulnerabilitiesCursor + try { + currentDomainVulnerabilitiesCursor = await trx.step( + () => this._query` + FOR finding IN additionalFindings + FILTER finding.domain == ${domain._id} + LIMIT 1 + FOR wc IN finding.webComponents + FILTER LENGTH(wc.WebComponentCves) > 0 + FOR vuln IN wc.WebComponentCves + FILTER vuln.Cve NOT IN ${newIgnoredCves} + RETURN DISTINCT vuln.Cve + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}" when getting current CVEs, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + UPDATE { _key: ${domain._key}, cveDetected: ${currentDomainVulnerabilitiesCursor.count > 0} } IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}" when updating domain, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Transaction commit error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + this.byKey.clear(domain._key) + return this.byKey.load(domain._key) + } + + async unignoreCve({ domain, ignoredCve, newIgnoredCves }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + UPSERT { _key: ${domain._key} } + INSERT ${{ ignoredCves: newIgnoredCves }} + UPDATE ${{ ignoredCves: newIgnoredCves }} + IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + let currentDomainVulnerabilitiesCursor + try { + currentDomainVulnerabilitiesCursor = await trx.step( + () => this._query` + FOR finding IN additionalFindings + FILTER finding.domain == ${domain._id} + LIMIT 1 + FOR wc IN finding.webComponents + FILTER LENGTH(wc.WebComponentCves) > 0 + FOR vuln IN wc.WebComponentCves + FILTER vuln.Cve NOT IN ${newIgnoredCves} + RETURN DISTINCT vuln.Cve + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}" when getting current CVEs, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + UPDATE { _key: ${domain._key}, cveDetected: ${currentDomainVulnerabilitiesCursor.count > 0} } IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}" when updating domain, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Transaction commit error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + this.byKey.clear(domain._key) + return this.byKey.load(domain._key) + } +} diff --git a/api/src/domain/helpers/__tests__/build-domain-filters.test.js b/api/src/domain/helpers/__tests__/build-domain-filters.test.js new file mode 100644 index 000000000..6bc33450a --- /dev/null +++ b/api/src/domain/helpers/__tests__/build-domain-filters.test.js @@ -0,0 +1,267 @@ +import { buildDomainFilters } from '../build-domain-filters' + +// Helper to extract the query string and bind vars from an aql result, +// stripping the accumulated prefix so assertions stay readable. +function parse(result) { + return { + query: result.query, + bindVars: result.bindVars, + } +} + +// ─── Empty / no-op cases ──────────────────────────────────────────────────── + +describe('buildDomainFilters – empty / no-op', () => { + it('returns an empty aql fragment when filters is undefined', () => { + const result = buildDomainFilters({}) + expect(result.query).toBe('') + expect(result.bindVars).toEqual({}) + }) + + it('returns an empty aql fragment when filters is null', () => { + const result = buildDomainFilters({ filters: null }) + expect(result.query).toBe('') + expect(result.bindVars).toEqual({}) + }) + + it('returns an empty aql fragment when filters is an empty array', () => { + const result = buildDomainFilters({ filters: [] }) + expect(result.query).toBe('') + expect(result.bindVars).toEqual({}) + }) +}) + +// ─── Status filters ───────────────────────────────────────────────────────── + +describe('buildDomainFilters – status filters (==)', () => { + const STATUS_CASES = [ + ['dmarc-status', 'v.status.dmarc'], + ['dkim-status', 'v.status.dkim'], + ['https-status', 'v.status.https'], + ['spf-status', 'v.status.spf'], + ['ciphers-status', 'v.status.ciphers'], + ['curves-status', 'v.status.curves'], + ['hsts-status', 'v.status.hsts'], + ['policy-status', 'v.status.policy'], + ['protocols-status', 'v.status.protocols'], + ['certificates-status', 'v.status.certificates'], + ] + + test.each(STATUS_CASES)('%s maps to %s with == comparison', (filterCategory, aqlField) => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [{ filterCategory, comparison: '==', filterValue: 'pass' }], + }), + ) + expect(query).toContain(`FILTER ${aqlField} ==`) + expect(Object.values(bindVars)).toContain('pass') + }) +}) + +describe('buildDomainFilters – status filters (!=)', () => { + it('uses != comparison operator for dmarc-status', () => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'dmarc-status', comparison: '!=', filterValue: 'fail' }], + }), + ) + expect(query).toContain('FILTER v.status.dmarc !=') + expect(Object.values(bindVars)).toContain('fail') + }) + + it('uses != comparison operator for https-status', () => { + const { query } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'https-status', comparison: '!=', filterValue: 'info' }], + }), + ) + expect(query).toContain('FILTER v.status.https !=') + }) +}) + +// ─── Tag filters – mapped fields ───────────────────────────────────────────── + +describe('buildDomainFilters – tags filter (mapped fields)', () => { + const TAG_CASES = [ + ['archived', 'v.archived', true], + ['nxdomain', 'v.rcode', 'NXDOMAIN'], + ['blocked', 'v.blocked', true], + ['wildcard-sibling', 'v.wildcardSibling', true], + ['wildcard-entry', 'v.wildcardEntry', true], + ['scan-pending', 'v.webScanPending', true], + ['has-entrust-certificate', 'v.hasEntrustCertificate', true], + ['cve-detected', 'v.cveDetected', true], + ['cvd-enrolled', 'v.cvdEnrollment.status', 'enrolled'], + ['cvd-pending', 'v.cvdEnrollment.status', 'pending'], + ] + + test.each(TAG_CASES)('tag value "%s" filters on field %s', (filterValue, aqlField) => { + const { query } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'tags', comparison: '==', filterValue }], + }), + ) + expect(query).toContain(`FILTER ${aqlField}`) + }) + + it('applies != comparison for a mapped tag (archived)', () => { + const { query } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'tags', comparison: '!=', filterValue: 'archived' }], + }), + ) + expect(query).toContain('FILTER v.archived !=') + }) +}) + +describe('buildDomainFilters – tags filter (unmapped / free-form)', () => { + it('falls back to POSITION(e.tags, ...) for an unknown tag value', () => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'tags', comparison: '==', filterValue: 'some-custom-tag' }], + }), + ) + expect(query).toContain('FILTER POSITION(e.tags,') + expect(query).toContain('== true') + expect(Object.values(bindVars)).toContain('some-custom-tag') + }) + + it('uses != with POSITION for an unknown tag with != comparison', () => { + const { query } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'tags', comparison: '!=', filterValue: 'another-tag' }], + }), + ) + expect(query).toContain('POSITION(e.tags,') + expect(query).toContain('!= true') + }) +}) + +// ─── Other filter categories ───────────────────────────────────────────────── + +describe('buildDomainFilters – asset-state filter', () => { + it('filters on e.assetState with == comparison', () => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'asset-state', comparison: '==', filterValue: 'approved' }], + }), + ) + expect(query).toContain('FILTER e.assetState ==') + expect(Object.values(bindVars)).toContain('approved') + }) + + it('filters on e.assetState with != comparison', () => { + const { query } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'asset-state', comparison: '!=', filterValue: 'hidden' }], + }), + ) + expect(query).toContain('FILTER e.assetState !=') + }) +}) + +describe('buildDomainFilters – guidance-tag filter', () => { + it('filters using POSITION(negativeTags, ...) with == comparison', () => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'guidance-tag', comparison: '==', filterValue: 'weak-cipher' }], + }), + ) + expect(query).toContain('FILTER POSITION(negativeTags,') + expect(query).toContain('== true') + expect(Object.values(bindVars)).toContain('weak-cipher') + }) + + it('filters using POSITION(negativeTags, ...) with != comparison', () => { + const { query } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'guidance-tag', comparison: '!=', filterValue: 'weak-cipher' }], + }), + ) + expect(query).toContain('FILTER POSITION(negativeTags,') + expect(query).toContain('!= true') + }) +}) + +describe('buildDomainFilters – dmarc-phase filter', () => { + it('filters on v.phase with == comparison', () => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'dmarc-phase', comparison: '==', filterValue: 'assess' }], + }), + ) + expect(query).toContain('FILTER v.phase ==') + expect(Object.values(bindVars)).toContain('assess') + }) + + it('filters on v.phase with != comparison', () => { + const { query } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'dmarc-phase', comparison: '!=', filterValue: 'deploy' }], + }), + ) + expect(query).toContain('FILTER v.phase !=') + }) +}) + +describe('buildDomainFilters – unknown filterCategory', () => { + it('silently skips an unrecognised filter category', () => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [{ filterCategory: 'totally-unknown', comparison: '==', filterValue: 'x' }], + }), + ) + // The accumulated fragment should be empty — nothing appended + expect(query).toBe('') + expect(bindVars).toEqual({}) + }) +}) + +// ─── Multiple filters combined ─────────────────────────────────────────────── + +describe('buildDomainFilters – multiple filters', () => { + it('accumulates all filters into a single AQL fragment', () => { + const { query, bindVars } = parse( + buildDomainFilters({ + filters: [ + { filterCategory: 'dmarc-status', comparison: '==', filterValue: 'pass' }, + { filterCategory: 'https-status', comparison: '!=', filterValue: 'fail' }, + { filterCategory: 'asset-state', comparison: '==', filterValue: 'approved' }, + ], + }), + ) + expect(query).toContain('FILTER v.status.dmarc ==') + expect(query).toContain('FILTER v.status.https !=') + expect(query).toContain('FILTER e.assetState ==') + expect(Object.values(bindVars)).toContain('pass') + expect(Object.values(bindVars)).toContain('fail') + expect(Object.values(bindVars)).toContain('approved') + }) + + it('combines a mapped tag filter with a status filter', () => { + const { query } = parse( + buildDomainFilters({ + filters: [ + { filterCategory: 'tags', comparison: '==', filterValue: 'blocked' }, + { filterCategory: 'spf-status', comparison: '==', filterValue: 'pass' }, + ], + }), + ) + expect(query).toContain('FILTER v.blocked') + expect(query).toContain('FILTER v.status.spf') + }) + + it('handles an unknown filter mixed with a valid one without losing the valid one', () => { + const { query } = parse( + buildDomainFilters({ + filters: [ + { filterCategory: 'dmarc-phase', comparison: '==', filterValue: 'assess' }, + { filterCategory: 'not-a-real-category', comparison: '==', filterValue: 'nope' }, + ], + }), + ) + expect(query).toContain('FILTER v.phase ==') + // The unknown category should not add anything extra + expect(query).not.toContain('nope') + }) +}) diff --git a/api/src/domain/helpers/build-domain-filters.js b/api/src/domain/helpers/build-domain-filters.js new file mode 100644 index 000000000..e43039d97 --- /dev/null +++ b/api/src/domain/helpers/build-domain-filters.js @@ -0,0 +1,121 @@ +import { aql } from 'arangojs' + +// Maps filter categories to their AQL field paths +const STATUS_FILTER_MAP = { + 'dmarc-status': 'v.status.dmarc', + 'dkim-status': 'v.status.dkim', + 'https-status': 'v.status.https', + 'spf-status': 'v.status.spf', + 'ciphers-status': 'v.status.ciphers', + 'curves-status': 'v.status.curves', + 'hsts-status': 'v.status.hsts', + 'policy-status': 'v.status.policy', + 'protocols-status': 'v.status.protocols', + 'certificates-status': 'v.status.certificates', +} + +// Maps tag filter values to { field, value } pairs for direct field comparisons +const TAG_FIELD_MAP = { + archived: { field: 'v.archived', value: true }, + nxdomain: { field: 'v.rcode', value: 'NXDOMAIN' }, + blocked: { field: 'v.blocked', value: true }, + 'wildcard-sibling': { field: 'v.wildcardSibling', value: true }, + 'wildcard-entry': { field: 'v.wildcardEntry', value: true }, + 'scan-pending': { field: 'v.webScanPending', value: true }, + 'has-entrust-certificate': { field: 'v.hasEntrustCertificate', value: true }, + 'cve-detected': { field: 'v.cveDetected', value: true }, + 'cvd-enrolled': { field: 'v.cvdEnrollment.status', value: 'enrolled' }, + 'cvd-pending': { field: 'v.cvdEnrollment.status', value: 'pending' }, + 'cvd-deny': { field: 'v.cvdEnrollment.status', value: 'deny' }, +} + +function buildComparison(comparison) { + return comparison === '==' ? aql`==` : aql`!=` +} + +function buildStatusFilter(accumulated, field, comparison, filterValue) { + // AQL doesn't support dynamic field access, so we need a switch here. + // The field string is only ever sourced from STATUS_FILTER_MAP above, never user input. + switch (field) { + case 'v.status.dmarc': + return aql`${accumulated} FILTER v.status.dmarc ${comparison} ${filterValue}` + case 'v.status.dkim': + return aql`${accumulated} FILTER v.status.dkim ${comparison} ${filterValue}` + case 'v.status.https': + return aql`${accumulated} FILTER v.status.https ${comparison} ${filterValue}` + case 'v.status.spf': + return aql`${accumulated} FILTER v.status.spf ${comparison} ${filterValue}` + case 'v.status.ciphers': + return aql`${accumulated} FILTER v.status.ciphers ${comparison} ${filterValue}` + case 'v.status.curves': + return aql`${accumulated} FILTER v.status.curves ${comparison} ${filterValue}` + case 'v.status.hsts': + return aql`${accumulated} FILTER v.status.hsts ${comparison} ${filterValue}` + case 'v.status.policy': + return aql`${accumulated} FILTER v.status.policy ${comparison} ${filterValue}` + case 'v.status.protocols': + return aql`${accumulated} FILTER v.status.protocols ${comparison} ${filterValue}` + case 'v.status.certificates': + return aql`${accumulated} FILTER v.status.certificates ${comparison} ${filterValue}` + default: + return accumulated + } +} + +function buildTagFilter(accumulated, comparison, filterValue) { + const mapped = TAG_FIELD_MAP[filterValue] + + if (!mapped) { + return aql`${accumulated} FILTER POSITION(e.tags, ${filterValue}) ${comparison} true` + } + + // Same dynamic field problem — switch on the known field paths from TAG_FIELD_MAP + switch (mapped.field) { + case 'v.archived': + return aql`${accumulated} FILTER v.archived ${comparison} ${mapped.value}` + case 'v.rcode': + return aql`${accumulated} FILTER v.rcode ${comparison} ${mapped.value}` + case 'v.blocked': + return aql`${accumulated} FILTER v.blocked ${comparison} ${mapped.value}` + case 'v.wildcardSibling': + return aql`${accumulated} FILTER v.wildcardSibling ${comparison} ${mapped.value}` + case 'v.wildcardEntry': + return aql`${accumulated} FILTER v.wildcardEntry ${comparison} ${mapped.value}` + case 'v.webScanPending': + return aql`${accumulated} FILTER v.webScanPending ${comparison} ${mapped.value}` + case 'v.hasEntrustCertificate': + return aql`${accumulated} FILTER v.hasEntrustCertificate ${comparison} ${mapped.value}` + case 'v.cveDetected': + return aql`${accumulated} FILTER v.cveDetected ${comparison} ${mapped.value}` + case 'v.cvdEnrollment.status': + return aql`${accumulated} FILTER v.cvdEnrollment.status ${comparison} ${mapped.value}` + default: + return accumulated + } +} + +function buildSingleFilter(accumulated, { filterCategory, comparison, filterValue }) { + const cmp = buildComparison(comparison) + + if (filterCategory in STATUS_FILTER_MAP) { + return buildStatusFilter(accumulated, STATUS_FILTER_MAP[filterCategory], cmp, filterValue) + } + + switch (filterCategory) { + case 'tags': + return buildTagFilter(accumulated, cmp, filterValue) + case 'asset-state': + return aql`${accumulated} FILTER e.assetState ${cmp} ${filterValue}` + case 'guidance-tag': + return aql`${accumulated} FILTER POSITION(negativeTags, ${filterValue}) ${cmp} true` + case 'dmarc-phase': + return aql`${accumulated} FILTER v.phase ${cmp} ${filterValue}` + default: + return accumulated + } +} + +export function buildDomainFilters({ filters }) { + if (!filters?.length) return aql`` + return filters.reduce(buildSingleFilter, aql``) +} diff --git a/api/src/domain/helpers/index.js b/api/src/domain/helpers/index.js new file mode 100644 index 000000000..2e6a27b84 --- /dev/null +++ b/api/src/domain/helpers/index.js @@ -0,0 +1 @@ +export * from './build-domain-filters' diff --git a/api/src/domain/index.js b/api/src/domain/index.js index 0f0e03e95..fe7380263 100644 --- a/api/src/domain/index.js +++ b/api/src/domain/index.js @@ -2,3 +2,4 @@ export * from './loaders' export * from './mutations' export * from './queries' export * from './objects' +export * from './data-source' diff --git a/api/src/domain/inputs/domain-filter.js b/api/src/domain/inputs/domain-filter.js index 2280a63b4..f69a4d4c6 100644 --- a/api/src/domain/inputs/domain-filter.js +++ b/api/src/domain/inputs/domain-filter.js @@ -1,22 +1,13 @@ -import { GraphQLInputObjectType, GraphQLEnumType } from 'graphql' -import { ComparisonEnums, DomainOrderField, DomainTagLabel, StatusEnum } from '../../enums' - -const filterValueEnumsVals = {} -const filterValueEnums = [...StatusEnum.getValues(), ...DomainTagLabel.getValues()] -filterValueEnums.forEach( - ({ name, value, description }) => - (filterValueEnumsVals[name] = { - value, - description, - }), -) +import { GraphQLInputObjectType } from 'graphql' +import { ComparisonEnums, DomainFilterCategory } from '../../enums' +import { FilterValueScalar } from '../../scalars/filter-value' export const domainFilter = new GraphQLInputObjectType({ name: 'DomainFilter', description: 'This object is used to provide filtering options when querying org-claimed domains.', fields: () => ({ filterCategory: { - type: DomainOrderField, + type: DomainFilterCategory, description: 'Category of filter to be applied.', }, comparison: { @@ -24,11 +15,7 @@ export const domainFilter = new GraphQLInputObjectType({ description: 'First value equals or does not equal second value.', }, filterValue: { - type: new GraphQLEnumType({ - name: 'filterValueEnums', - values: filterValueEnumsVals, - description: '', - }), + type: FilterValueScalar, description: 'Status type or tag label.', }, }), diff --git a/api/src/domain/inputs/domain-tag.js b/api/src/domain/inputs/domain-tag.js deleted file mode 100644 index 3305abaf4..000000000 --- a/api/src/domain/inputs/domain-tag.js +++ /dev/null @@ -1,17 +0,0 @@ -import { GraphQLInputObjectType, GraphQLNonNull } from 'graphql' -import { DomainTagLabel } from '../../enums' - -export const inputTag = new GraphQLInputObjectType({ - name: 'InputTag', - description: 'User-generated tag assigned to domains for labeling and management.', - fields: () => ({ - en: { - type: new GraphQLNonNull(DomainTagLabel), - description: 'The English translation of the label.', - }, - fr: { - type: new GraphQLNonNull(DomainTagLabel), - description: 'The French translation of the label.', - }, - }), -}) diff --git a/api/src/domain/inputs/index.js b/api/src/domain/inputs/index.js index 07810848e..a1b014c4b 100644 --- a/api/src/domain/inputs/index.js +++ b/api/src/domain/inputs/index.js @@ -1,3 +1,2 @@ export * from './domain-filter' export * from './domain-order' -export * from './domain-tag' diff --git a/api/src/domain/loaders/__tests__/load-domain-by-domain.test.js b/api/src/domain/loaders/__tests__/load-domain-by-domain.test.js index 30d039dfa..2e43e661e 100644 --- a/api/src/domain/loaders/__tests__/load-domain-by-domain.test.js +++ b/api/src/domain/loaders/__tests__/load-domain-by-domain.test.js @@ -1,12 +1,13 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' -import {loadDomainByDomain} from '../index' +import { loadDomainByDomain } from '../index' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadDomainByDomain dataloader', () => { let query, drop, truncate, collections, i18n @@ -20,7 +21,7 @@ describe('given a loadDomainByDomain dataloader', () => { }) describe('given a successful load', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -56,7 +57,7 @@ describe('given a loadDomainByDomain dataloader', () => { ` const expectedDomain = await expectedCursor.next() - const loader = loadDomainByDomain({query}) + const loader = loadDomainByDomain({ query }) const domain = await loader.load(expectedDomain.domain) expect(domain).toEqual(expectedDomain) @@ -77,7 +78,7 @@ describe('given a loadDomainByDomain dataloader', () => { expectedDomains.push(tempDomain) } - const loader = loadDomainByDomain({query}) + const loader = loadDomainByDomain({ query }) const domains = await loader.loadMany(domainDomains) expect(domains).toEqual(expectedDomains) }) @@ -100,9 +101,7 @@ describe('given a loadDomainByDomain dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadDomainByDomain({ query: mockedQuery, userKey: '1234', @@ -112,9 +111,7 @@ describe('given a loadDomainByDomain dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load domain. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load domain. Please try again.')) } expect(consoleOutput).toEqual([ @@ -139,9 +136,7 @@ describe('given a loadDomainByDomain dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load domain. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load domain. Please try again.')) } expect(consoleOutput).toEqual([ @@ -167,9 +162,7 @@ describe('given a loadDomainByDomain dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadDomainByDomain({ query: mockedQuery, userKey: '1234', @@ -179,9 +172,7 @@ describe('given a loadDomainByDomain dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Impossible de charger le domaine. Veuillez réessayer.'), - ) + expect(err).toEqual(new Error('Impossible de charger le domaine. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -206,9 +197,7 @@ describe('given a loadDomainByDomain dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Impossible de charger le domaine. Veuillez réessayer.'), - ) + expect(err).toEqual(new Error('Impossible de charger le domaine. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/domain/loaders/__tests__/load-domain-by-key.test.js b/api/src/domain/loaders/__tests__/load-domain-by-key.test.js index 348984369..761eca50e 100644 --- a/api/src/domain/loaders/__tests__/load-domain-by-key.test.js +++ b/api/src/domain/loaders/__tests__/load-domain-by-key.test.js @@ -1,12 +1,13 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' -import {loadDomainByKey} from '../index' +import { loadDomainByKey } from '../index' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadDomainByKey dataloader', () => { let query, drop, truncate, collections, i18n @@ -22,7 +23,7 @@ describe('given a loadDomainByKey dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -60,7 +61,7 @@ describe('given a loadDomainByKey dataloader', () => { ` const expectedDomain = await expectedCursor.next() - const loader = loadDomainByKey({query}) + const loader = loadDomainByKey({ query }) const domain = await loader.load(expectedDomain._key) expect(domain).toEqual(expectedDomain) @@ -81,7 +82,7 @@ describe('given a loadDomainByKey dataloader', () => { expectedDomains.push(tempDomain) } - const loader = loadDomainByKey({query}) + const loader = loadDomainByKey({ query }) const domains = await loader.loadMany(domainIds) expect(domains).toEqual(expectedDomains) }) @@ -92,8 +93,8 @@ describe('given a loadDomainByKey dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -104,9 +105,7 @@ describe('given a loadDomainByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadDomainByKey({ query: mockedQuery, userKey: '1234', @@ -116,9 +115,7 @@ describe('given a loadDomainByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load domain. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load domain. Please try again.')) } expect(consoleOutput).toEqual([ @@ -143,9 +140,7 @@ describe('given a loadDomainByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load domain. Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load domain. Please try again.')) } expect(consoleOutput).toEqual([ @@ -159,8 +154,8 @@ describe('given a loadDomainByKey dataloader', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -171,9 +166,7 @@ describe('given a loadDomainByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadDomainByKey({ query: mockedQuery, userKey: '1234', @@ -183,9 +176,7 @@ describe('given a loadDomainByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Impossible de charger le domaine. Veuillez réessayer.'), - ) + expect(err).toEqual(new Error('Impossible de charger le domaine. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -210,9 +201,7 @@ describe('given a loadDomainByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Impossible de charger le domaine. Veuillez réessayer.'), - ) + expect(err).toEqual(new Error('Impossible de charger le domaine. Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/domain/loaders/__tests__/load-domain-conn-org-id.test.js b/api/src/domain/loaders/__tests__/load-domain-conn-org-id.test.js index 4d7676890..b6551a859 100644 --- a/api/src/domain/loaders/__tests__/load-domain-conn-org-id.test.js +++ b/api/src/domain/loaders/__tests__/load-domain-conn-org-id.test.js @@ -1,27 +1,19 @@ -import {stringify} from 'jest-matcher-utils' -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { stringify } from 'jest-matcher-utils' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' -import {cleanseInput} from '../../../validators' -import {loadDomainConnectionsByOrgId, loadDomainByKey} from '../index' -import {toGlobalId} from 'graphql-relay' +import { cleanseInput } from '../../../validators' +import { loadDomainConnectionsByOrgId, loadDomainByKey } from '../index' +import { toGlobalId } from 'graphql-relay' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the load domain connection using org id function', () => { - let query, - drop, - truncate, - collections, - user, - org, - domain, - domainTwo, - i18n, - language + let query, drop, truncate, collections, user, org, domain, domainTwo, i18n, language const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -37,7 +29,7 @@ describe('given the load domain connection using org id function', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -54,7 +46,6 @@ describe('given the load domain connection using org id function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -104,7 +95,7 @@ describe('given the load domain connection using org id function', () => { _from: org._id, _to: domain._id, tags: [], - hidden: false, + assetState: 'approved', }) domainTwo = await collections.domains.save({ domain: 'test.domain.canada.ca', @@ -123,7 +114,7 @@ describe('given the load domain connection using org id function', () => { _from: org._id, _to: domainTwo._id, tags: [], - hidden: false, + assetState: 'approved', }) }) afterEach(async () => { @@ -139,14 +130,11 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -167,7 +155,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -190,14 +178,11 @@ describe('given the load domain connection using org id function', () => { language, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -218,7 +203,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -241,14 +226,11 @@ describe('given the load domain connection using org id function', () => { language, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -268,7 +250,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -291,14 +273,11 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -318,7 +297,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -350,10 +329,10 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) - const domainLoader = loadDomainByKey({query}) + const domainLoader = loadDomainByKey({ query }) const expectedDomain = await domainLoader.load(domain._key) const connectionArgs = { @@ -362,7 +341,7 @@ describe('given the load domain connection using org id function', () => { search: 'test.domain.gc.ca', } - const domains = await connectionLoader({...connectionArgs}) + const domains = await connectionLoader({ ...connectionArgs }) const expectedStructure = { edges: [ @@ -371,7 +350,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomain, claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -395,7 +374,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const connectionArgs = { @@ -430,7 +409,7 @@ describe('given the load domain connection using org id function', () => { _from: org._id, _to: domainThree._id, tags: [], - hidden: false, + assetState: 'approved', }) await collections.ownership.save({ _from: org._id, @@ -444,13 +423,11 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequiredBool: true}, + auth: { loginRequiredBool: true }, }) - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domainThree._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domainThree._key]) expectedDomains[0].id = expectedDomains[0]._key @@ -470,7 +447,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -493,15 +470,11 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - domainThree._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key, domainThree._key]) const connectionArgs = { first: 5, @@ -519,7 +492,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, { @@ -527,7 +500,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, { @@ -535,7 +508,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[2], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -557,11 +530,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on DOMAIN', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -579,7 +549,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -593,7 +563,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -611,11 +581,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -633,7 +600,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -648,7 +615,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -668,11 +635,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on DKIM_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -690,7 +654,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -705,7 +669,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -723,11 +687,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -745,7 +706,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -760,7 +721,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -780,11 +741,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on DMARC_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -802,7 +760,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -817,7 +775,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -835,11 +793,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -857,7 +812,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -872,7 +827,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -892,11 +847,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on HTTPS_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -914,7 +866,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -929,7 +881,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -947,11 +899,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -969,7 +918,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -984,7 +933,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1004,11 +953,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on SPF_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1026,7 +972,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1041,7 +987,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1059,11 +1005,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1081,7 +1024,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1096,7 +1039,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1118,11 +1061,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on DOMAIN', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1140,7 +1080,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1155,7 +1095,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1173,11 +1113,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1195,7 +1132,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1210,7 +1147,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1230,11 +1167,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on DKIM_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1252,7 +1186,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1267,7 +1201,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1285,11 +1219,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1307,7 +1238,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1322,7 +1253,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1342,11 +1273,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on DMARC_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1364,7 +1292,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1379,7 +1307,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1397,11 +1325,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1419,7 +1344,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1434,7 +1359,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1454,11 +1379,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on HTTPS_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1476,7 +1398,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1491,7 +1413,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1509,11 +1431,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1531,7 +1450,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1546,7 +1465,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1566,11 +1485,8 @@ describe('given the load domain connection using org id function', () => { describe('ordering on SPF_STATUS', () => { describe('order direction is ASC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1588,7 +1504,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1603,7 +1519,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[1], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1621,11 +1537,8 @@ describe('given the load domain connection using org id function', () => { }) describe('order direction is DESC', () => { it('returns domains in order', async () => { - const domainLoader = loadDomainByKey({query}) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1643,7 +1556,7 @@ describe('given the load domain connection using org id function', () => { userKey: user._key, language, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, }) const domains = await connectionLoader({ orgId: org._id, @@ -1658,7 +1571,7 @@ describe('given the load domain connection using org id function', () => { node: { ...expectedDomains[0], claimTags: [], - hidden: false, + assetState: 'approved', }, }, ], @@ -1683,8 +1596,8 @@ describe('given the load domain connection using org id function', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -1700,7 +1613,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1729,7 +1642,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1744,9 +1657,7 @@ describe('given the load domain connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `Passing both \`first\` and \`last\` to paginate the \`Domain\` connection is not supported.`, - ), + new Error(`Passing both \`first\` and \`last\` to paginate the \`Domain\` connection is not supported.`), ) } @@ -1762,7 +1673,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1775,11 +1686,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` on the \`Domain\` connection cannot be less than zero.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` on the \`Domain\` connection cannot be less than zero.`)) } expect(consoleOutput).toEqual([ @@ -1793,7 +1700,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1806,11 +1713,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` on the \`Domain\` connection cannot be less than zero.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` on the \`Domain\` connection cannot be less than zero.`)) } expect(consoleOutput).toEqual([ @@ -1826,7 +1729,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1857,7 +1760,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1886,14 +1789,12 @@ describe('given the load domain connection using org id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDomainConnectionsByOrgId({ query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1907,11 +1808,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -1923,14 +1820,12 @@ describe('given the load domain connection using org id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDomainConnectionsByOrgId({ query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1944,11 +1839,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -1963,15 +1854,13 @@ describe('given the load domain connection using org id function', () => { describe('given a database error', () => { describe('when gathering domain keys that are claimed by orgs that the user has affiliations to', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database Error Occurred.')) const connectionLoader = loadDomainConnectionsByOrgId({ query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -1984,9 +1873,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -2009,7 +1896,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2022,9 +1909,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -2039,8 +1924,8 @@ describe('given the load domain connection using org id function', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -2056,7 +1941,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2085,7 +1970,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2100,9 +1985,7 @@ describe('given the load domain connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - "Passer à la fois `first` et `last` pour paginer la connexion `Domain` n'est pas supporté.", - ), + new Error("Passer à la fois `first` et `last` pour paginer la connexion `Domain` n'est pas supporté."), ) } @@ -2118,7 +2001,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2131,11 +2014,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` sur la connexion `Domain` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`first` sur la connexion `Domain` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ @@ -2149,7 +2028,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2162,11 +2041,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` sur la connexion `Domain` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`last` sur la connexion `Domain` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ @@ -2182,7 +2057,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2213,7 +2088,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2242,14 +2117,12 @@ describe('given the load domain connection using org id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDomainConnectionsByOrgId({ query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2264,9 +2137,7 @@ describe('given the load domain connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -2279,14 +2150,12 @@ describe('given the load domain connection using org id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadDomainConnectionsByOrgId({ query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2301,9 +2170,7 @@ describe('given the load domain connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -2319,15 +2186,13 @@ describe('given the load domain connection using org id function', () => { describe('given a database error', () => { describe('when gathering domain keys that are claimed by orgs that the user has affiliations to', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database Error Occurred.')) const connectionLoader = loadDomainConnectionsByOrgId({ query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2340,11 +2205,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -2367,7 +2228,7 @@ describe('given the load domain connection using org id function', () => { query, userKey: user._key, cleanseInput, - auth: {loginRequired: true}, + auth: { loginRequired: true }, i18n, }) @@ -2380,11 +2241,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/domain/loaders/__tests__/load-domain-connections-by-user-id.test.js b/api/src/domain/loaders/__tests__/load-domain-connections-by-user-id.test.js index e0871384e..0286f4ae0 100644 --- a/api/src/domain/loaders/__tests__/load-domain-connections-by-user-id.test.js +++ b/api/src/domain/loaders/__tests__/load-domain-connections-by-user-id.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -44,7 +45,6 @@ describe('given the load domain connections by user id function', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -1411,6 +1411,54 @@ describe('given the load domain connections by user id function', () => { }, } + expect(domains).toEqual(expectedStructure) + }) + }) + describe('isAffiliated is set to true', () => { + it('returns a domain', async () => { + const connectionLoader = loadDomainConnectionsByUserId({ + query, + userKey: user._key, + cleanseInput, + auth: { loginRequired: true }, + }) + + const connectionArgs = { + first: 10, + isAffiliated: true, + } + const domains = await connectionLoader({ ...connectionArgs }) + + const domainLoader = loadDomainByKey({ query }) + const expectedDomains = await domainLoader.loadMany([domainOne._key, domainTwo._key]) + + expectedDomains[0].id = expectedDomains[0]._key + expectedDomains[1].id = expectedDomains[1]._key + + const expectedStructure = { + edges: [ + { + cursor: toGlobalId('domain', expectedDomains[0]._key), + node: { + ...expectedDomains[0], + }, + }, + { + cursor: toGlobalId('domain', expectedDomains[1]._key), + node: { + ...expectedDomains[1], + }, + }, + ], + totalCount: 2, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: toGlobalId('domain', expectedDomains[0]._key), + endCursor: toGlobalId('domain', expectedDomains[1]._key), + }, + } + expect(domains).toEqual(expectedStructure) }) }) diff --git a/api/src/domain/loaders/__tests__/load-domain-tags-by-org-id.test.js b/api/src/domain/loaders/__tests__/load-domain-tags-by-org-id.test.js deleted file mode 100644 index 977766698..000000000 --- a/api/src/domain/loaders/__tests__/load-domain-tags-by-org-id.test.js +++ /dev/null @@ -1,239 +0,0 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import {loadDomainTagsByOrgId} from '../index' -import dbschema from '../../../../database.json' - -const {DB_PASS: rootPass, DB_URL: url} = process.env - -describe('given the load domain connection using org id function', () => { - let query, drop, truncate, collections, user, org, domain, domainTwo, i18n - - const consoleOutput = [] - const mockedError = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - - beforeAll(async () => { - console.error = mockedError - console.warn = mockedWarn - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('given a successful load', () => { - beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - user = await collections.users.save({ - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'french', - tfaValidated: false, - emailValidated: false, - }) - org = await collections.organizations.save({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }) - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'admin', - }) - domain = await collections.domains.save({ - domain: 'test.domain.gc.ca', - lastRan: '2021-01-02 12:12:12.000000', - selectors: ['selector1', 'selector2'], - status: { - dkim: 'pass', - dmarc: 'pass', - https: 'pass', - spf: 'pass', - ssl: 'pass', - }, - tags: [ - { - id: 'CVE-2022-12345', - firstDetected: '2021-01-02 12:12:12.000000', - severity: 'high', - }, - ], - }) - await collections.claims.save({ - _from: org._id, - _to: domain._id, - }) - domainTwo = await collections.domains.save({ - domain: 'test.domain.canada.ca', - lastRan: '2022-06-07 12:12:12.000000', - selectors: ['selector1', 'selector2'], - status: { - dkim: 'fail', - dmarc: 'fail', - https: 'fail', - spf: 'fail', - ssl: 'fail', - }, - tags: [ - { - id: 'CVE-2022-54321', - firstDetected: '2022-06-07 12:12:12.000000', - severity: 'low', - }, - ], - }) - await collections.claims.save({ - _from: org._id, - _to: domainTwo._id, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - describe('using loader', () => { - it('returns a list of tags', async () => { - const connectionLoader = loadDomainTagsByOrgId({ - query, - userKey: user._key, - i18n, - }) - - const tags = await connectionLoader({ - orgId: org._id, - }) - - const expectedStructure = { - edges: [ - {id: 'CVE-2022-12345', severity: 'high'}, - {id: 'CVE-2022-54321', severity: 'low'}, - ], - totalCount: 2, - } - - expect(tags).toEqual(expectedStructure) - }) - }) - describe('no tags are found', () => { - it('returns an empty structure', async () => { - await truncate() - const connectionLoader = loadDomainTagsByOrgId({ - query, - userKey: user._key, - i18n, - }) - - const tags = await connectionLoader({ - orgId: 'notanid', - }) - - const expectedStructure = { - edges: [], - totalCount: 0, - } - - expect(tags).toEqual(expectedStructure) - }) - }) - }) - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given an unsuccessful load', () => { - it('returns an error message', async () => { - const connectionLoader = loadDomainTagsByOrgId({ - query, - userKey: user._key, - i18n, - }) - - try { - await connectionLoader({ - orgId: org._id, - }) - } catch (err) { - expect(err).toEqual( - new Error(`Unable to load domain(s). Please try again.`), - ) - } - }) - }) - describe('given a database error', () => { - describe('when gathering domain keys that are claimed by orgs that the user has affiliations to', () => { - it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) - - const connectionLoader = loadDomainTagsByOrgId({ - query, - userKey: user._key, - i18n, - }) - - const connectionArgs = { - first: 5, - } - try { - await connectionLoader({ - orgId: org._id, - ...connectionArgs, - }) - } catch (err) { - expect(err).toEqual( - new Error('Unable to load domain(s). Please try again.'), - ) - } - }) - }) - }) - }) -}) diff --git a/api/src/domain/loaders/index.js b/api/src/domain/loaders/index.js index c837b9a72..a34780f2e 100644 --- a/api/src/domain/loaders/index.js +++ b/api/src/domain/loaders/index.js @@ -3,4 +3,3 @@ export * from './load-domain-by-key' export * from './load-domain-connections-by-organizations-id' export * from './load-domain-connections-by-user-id' export * from './load-dkim-selectors-by-domain-id' -export * from './load-domain-tags-by-org-id' diff --git a/api/src/domain/loaders/load-domain-connections-by-organizations-id.js b/api/src/domain/loaders/load-domain-connections-by-organizations-id.js index d843a093f..14dd6d7a5 100644 --- a/api/src/domain/loaders/load-domain-connections-by-organizations-id.js +++ b/api/src/domain/loaders/load-domain-connections-by-organizations-id.js @@ -1,11 +1,11 @@ import { aql } from 'arangojs' import { fromGlobalId, toGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' +import { buildDomainFilters } from '../helpers' export const loadDomainConnectionsByOrgId = - ({ query, userKey, language, cleanseInput, i18n, auth: { loginRequiredBool } }) => + ({ query, userKey, cleanseInput, i18n, auth: { loginRequiredBool } }) => async ({ orgId, permission, after, before, first, last, ownership, orderBy, search, filters = [] }) => { - const userDBId = `users/${userKey}` let afterTemplate = aql`` let afterVar = aql`` @@ -289,215 +289,68 @@ export const loadDomainConnectionsByOrgId = } } - let sortString - if (typeof last !== 'undefined') { - sortString = aql`DESC` - } else { - sortString = aql`ASC` - } - - let domainFilters = aql` - LET hidden = ( - FOR v, e IN 1..1 ANY domain._id claims - FILTER e._from == ${orgId} - RETURN e.hidden - )[0] - LET claimTags = ( - FOR v, e IN 1..1 ANY domain._id claims - FILTER e._from == ${orgId} - LET translatedTags = ( - FOR tag IN e.tags || [] - RETURN TRANSLATE(${language}, tag) - ) - RETURN translatedTags - )[0] - ` - if (typeof filters !== 'undefined') { - filters.forEach(({ filterCategory, comparison, filterValue }) => { - if (comparison === '==') { - comparison = aql`==` - } else { - comparison = aql`!=` - } - if (filterCategory === 'dmarc-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.dmarc ${comparison} ${filterValue} - ` - } else if (filterCategory === 'dkim-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.dkim ${comparison} ${filterValue} - ` - } else if (filterCategory === 'https-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.https ${comparison} ${filterValue} - ` - } else if (filterCategory === 'spf-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.spf ${comparison} ${filterValue} - ` - } else if (filterCategory === 'ciphers-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.ciphers ${comparison} ${filterValue} - ` - } else if (filterCategory === 'curves-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.curves ${comparison} ${filterValue} - ` - } else if (filterCategory === 'hsts-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.hsts ${comparison} ${filterValue} - ` - } else if (filterCategory === 'policy-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.policy ${comparison} ${filterValue} - ` - } else if (filterCategory === 'protocols-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.protocols ${comparison} ${filterValue} - ` - } else if (filterCategory === 'certificates-status') { - domainFilters = aql` - ${domainFilters} - FILTER domain.status.certificates ${comparison} ${filterValue} - ` - } else if (filterCategory === 'tags') { - if (filterValue === 'hidden') { - domainFilters = aql` - ${domainFilters} - FILTER hidden ${comparison} true - ` - } else if (filterValue === 'archived') { - domainFilters = aql` - ${domainFilters} - FILTER domain.archived ${comparison} true - ` - } else if (filterValue === 'nxdomain') { - domainFilters = aql` - ${domainFilters} - FILTER domain.rcode ${comparison} "NXDOMAIN" - ` - } else if (filterValue === 'blocked') { - domainFilters = aql` - ${domainFilters} - FILTER domain.blocked ${comparison} true - ` - } else if (filterValue === 'wildcard-sibling') { - domainFilters = aql` - ${domainFilters} - FILTER domain.wildcardSibling ${comparison} true - ` - } else if (filterValue === 'scan-pending') { - domainFilters = aql` - ${domainFilters} - FILTER domain.webScanPending ${comparison} true - ` - } else { - domainFilters = aql` - ${domainFilters} - FILTER POSITION( claimTags, ${filterValue}) ${comparison} true - ` - } - } - }) - } + const domainFilters = buildDomainFilters({ filters }) let domainQuery = aql`` - let loopString = aql`FOR domain IN domains` - let totalCount = aql`LENGTH(domainKeys)` + let loopString = aql`FOR domain IN collectedDomains` + let totalCount = aql`LENGTH(collectedDomains)` if (typeof search !== 'undefined' && search !== '') { search = cleanseInput(search) domainQuery = aql` - LET tokenArr = TOKENS(${search}, "space-delimiter-analyzer") - LET searchedDomains = ( - FOR tokenItem IN tokenArr - LET token = LOWER(tokenItem) - FOR domain IN domainSearch - SEARCH ANALYZER(domain.domain LIKE CONCAT("%", token, "%"), "space-delimiter-analyzer") - FILTER domain._key IN domainKeys + LET searchedDomains = ( + FOR domain IN collectedDomains + FILTER LOWER(domain.domain) LIKE LOWER(${search}) RETURN MERGE({ id: domain._key, _type: "domain" }, domain) - ) - ` + ) + ` loopString = aql`FOR domain IN searchedDomains` totalCount = aql`LENGTH(searchedDomains)` } - let showArchivedDomains = aql`FILTER domain.archived != true` + let showArchivedDomains = aql`FILTER v.archived != true` if (permission === 'super_admin') { showArchivedDomains = aql`` } - let showHiddenDomains = aql`FILTER e.hidden != true` - if (['super_admin', 'owner', 'admin', 'user'].includes(permission)) { - showHiddenDomains = aql`` - } let ownershipOrgsOnly = aql` - LET claimKeys = ( - FOR v, e IN 1..1 OUTBOUND ${orgId} claims - OPTIONS {order: "bfs"} - ${showHiddenDomains} - RETURN v._key - ) + FOR v, e IN 1..1 OUTBOUND ${orgId} claims + OPTIONS {order: "bfs"} + RETURN v ` if (typeof ownership !== 'undefined') { if (ownership) { ownershipOrgsOnly = aql` - LET claimKeys = ( - FOR v, e IN 1..1 OUTBOUND ${orgId} ownership - OPTIONS {order: "bfs"} - ${showHiddenDomains} - RETURN v._key - ) + FOR v, e IN 1..1 OUTBOUND ${orgId} ownership + OPTIONS {order: "bfs"} + RETURN v ` } } let requestedDomainInfo try { - let domainKeysQuery + let collectedDomains if (!loginRequiredBool) { - domainKeysQuery = aql` - LET domainKeys = ( + collectedDomains = aql` + LET collectedDomains = ( FOR v, e IN 1..1 OUTBOUND ${orgId} claims OPTIONS {order: "bfs"} - ${showHiddenDomains} - RETURN v._key + LET negativeTags = APPEND(v.negativeTags.dns, v.negativeTags.web) + ${showArchivedDomains} + ${domainFilters} + RETURN v )` } else { - domainKeysQuery = aql` - LET domainKeys = UNIQUE(FLATTEN( - LET superAdmin = ( - FOR v, e IN 1 INBOUND ${userDBId} affiliations - OPTIONS {order: "bfs"} - FILTER e.permission == "super_admin" - RETURN e.permission - ) - LET affiliationKeys = ( - FOR v, e IN 1..1 INBOUND ${userDBId} affiliations - OPTIONS {order: "bfs"} - RETURN v._key - ) - LET superAdminOrgs = (FOR org IN organizations RETURN org._key) - LET keys = ('super_admin' IN superAdmin ? superAdminOrgs : affiliationKeys) + collectedDomains = aql` + LET collectedDomains = UNIQUE( ${ownershipOrgsOnly} - - LET orgKeys = INTERSECTION(keys, claimKeys) - RETURN claimKeys - ))` + )` } requestedDomainInfo = await query` WITH affiliations, domains, organizations, users - ${domainKeysQuery} + ${collectedDomains} ${domainQuery} @@ -506,35 +359,30 @@ export const loadDomainConnectionsByOrgId = LET retrievedDomains = ( ${loopString} - FILTER domain._key IN domainKeys - ${showArchivedDomains} - ${domainFilters} - + LET claimVals = ( + FOR v, e IN 1..1 ANY domain._id claims + FILTER e._from == ${orgId} + RETURN { assetState: e.assetState, claimTags: e.tags } + )[0] ${afterTemplate} ${beforeTemplate} SORT ${sortByField} ${limitTemplate} - RETURN MERGE({ id: domain._key, _type: "domain", "claimTags": claimTags, "hidden": hidden }, DOCUMENT(domain._id)) + RETURN MERGE({ id: domain._key, _type: "domain", "claimTags": claimVals.claimTags, "assetState": claimVals.assetState }, DOCUMENT(domain._id)) ) LET hasNextPage = (LENGTH( ${loopString} - FILTER domain._key IN domainKeys - ${showArchivedDomains} - ${domainFilters} ${hasNextPageFilter} - SORT ${sortByField} TO_NUMBER(domain._key) ${sortString} LIMIT 1 + LIMIT 1 RETURN domain ) > 0 ? true : false) LET hasPreviousPage = (LENGTH( ${loopString} - FILTER domain._key IN domainKeys - ${showArchivedDomains} - ${domainFilters} ${hasPreviousPageFilter} - SORT ${sortByField} TO_NUMBER(domain._key) ${sortString} LIMIT 1 + LIMIT 1 RETURN domain ) > 0 ? true : false) diff --git a/api/src/domain/loaders/load-domain-connections-by-user-id.js b/api/src/domain/loaders/load-domain-connections-by-user-id.js index cf51e8e7a..b15b39d90 100644 --- a/api/src/domain/loaders/load-domain-connections-by-user-id.js +++ b/api/src/domain/loaders/load-domain-connections-by-user-id.js @@ -1,10 +1,23 @@ import { aql } from 'arangojs' import { fromGlobalId, toGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' +import { buildDomainFilters } from '../helpers' export const loadDomainConnectionsByUserId = ({ query, userKey, cleanseInput, i18n, auth: { loginRequiredBool } }) => - async ({ after, before, first, last, ownership, orderBy, isSuperAdmin, myTracker, search }) => { + async ({ + after, + before, + first, + last, + ownership, + orderBy, + isSuperAdmin, + myTracker, + search, + isAffiliated, + filters = [], + }) => { const userDBId = `users/${userKey}` let ownershipOrgsOnly = aql` @@ -303,12 +316,7 @@ export const loadDomainConnectionsByUserId = } } - let sortString - if (typeof last !== 'undefined') { - sortString = aql`DESC` - } else { - sortString = aql`ASC` - } + const domainFilters = buildDomainFilters({ filters }) let domainKeysQuery if (myTracker) { @@ -326,7 +334,25 @@ export const loadDomainConnectionsByUserId = LET collectedDomains = UNIQUE( FOR org IN organizations ${ownershipOrgsOnly} + ${domainFilters} + RETURN v + ) + ` + } else if (isAffiliated) { + domainKeysQuery = aql` + WITH affiliations, domains, organizations, users, domainSearch, claims, ownership + LET collectedDomains = UNIQUE( + LET userAffiliations = ( + FOR v, e IN 1..1 INBOUND ${userDBId} affiliations + FILTER e.permission != "pending" RETURN v + ) + FOR org IN organizations + FILTER org._key IN userAffiliations[*]._key + ${ownershipOrgsOnly} + FILTER v.archived != true + ${domainFilters} + RETURN v ) ` } else if (!loginRequiredBool) { @@ -342,6 +368,7 @@ export const loadDomainConnectionsByUserId = FILTER org._key IN userAffiliations[*]._key || org.verified == true ${ownershipOrgsOnly} FILTER v.archived != true + ${domainFilters} RETURN v ) ` @@ -360,6 +387,7 @@ export const loadDomainConnectionsByUserId = FILTER org._key IN userAffiliations[*]._key || (hasVerifiedOrgAffiliation == true && org.verified == true) ${ownershipOrgsOnly} FILTER v.archived != true + ${domainFilters} RETURN v ) ` @@ -371,24 +399,16 @@ export const loadDomainConnectionsByUserId = if (typeof search !== 'undefined' && search !== '') { search = cleanseInput(search) domainQuery = aql` - LET tokenArr = TOKENS(${search}, "space-delimiter-analyzer") LET searchedDomains = ( - FOR tokenItem in tokenArr - LET token = LOWER(tokenItem) - FOR domain IN domainSearch - SEARCH ANALYZER(domain.domain LIKE CONCAT("%", token, "%"), "space-delimiter-analyzer") - FILTER domain IN collectedDomains - RETURN domain + FOR domain IN collectedDomains + FILTER LOWER(domain.domain) LIKE LOWER(${search}) + RETURN domain ) ` loopString = aql`FOR domain IN searchedDomains` totalCount = aql`LENGTH(searchedDomains)` } - let showArchivedDomains = aql`FILTER domain.archived != true` - if (isSuperAdmin) { - showArchivedDomains = aql`` - } let requestedDomainInfo try { requestedDomainInfo = await query` @@ -401,7 +421,6 @@ export const loadDomainConnectionsByUserId = LET retrievedDomains = ( ${loopString} - ${showArchivedDomains} ${afterTemplate} ${beforeTemplate} SORT @@ -412,17 +431,15 @@ export const loadDomainConnectionsByUserId = LET hasNextPage = (LENGTH( ${loopString} - ${showArchivedDomains} ${hasNextPageFilter} - SORT ${sortByField} TO_NUMBER(domain._key) ${sortString} LIMIT 1 + LIMIT 1 RETURN domain ) > 0 ? true : false) LET hasPreviousPage = (LENGTH( ${loopString} - ${showArchivedDomains} ${hasPreviousPageFilter} - SORT ${sortByField} TO_NUMBER(domain._key) ${sortString} LIMIT 1 + LIMIT 1 RETURN domain ) > 0 ? true : false) diff --git a/api/src/domain/loaders/load-domain-tags-by-org-id.js b/api/src/domain/loaders/load-domain-tags-by-org-id.js deleted file mode 100644 index a568043d7..000000000 --- a/api/src/domain/loaders/load-domain-tags-by-org-id.js +++ /dev/null @@ -1,55 +0,0 @@ -import {t} from '@lingui/macro' - -export const loadDomainTagsByOrgId = - ({query, userKey, i18n}) => - async ({orgId}) => { - let requestedTagsInfo - try { - requestedTagsInfo = await query` - WITH organizations - LET domainKeys = ( - FOR v, e IN 1..1 OUTBOUND ${orgId} claims - OPTIONS {order: "bfs"} - RETURN v._key - ) - LET orgTags = UNIQUE( - FOR d in domains - FILTER d._key in domainKeys - FOR tag in d.tags - RETURN { id: tag.id, severity: tag.severity } - ) - RETURN { "tags": orgTags, "totalCount": LENGTH(orgTags) } - ` - } catch (err) { - console.error( - `Database error occurred while user: ${userKey} was trying to gather domains in loadDomainTagsByOrgId, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to load domain(s). Please try again.`)) - } - - let tagsInfo - try { - tagsInfo = await requestedTagsInfo.next() - } catch (err) { - console.error( - `Error occurred while user: ${userKey} was trying to gather domainTags in loadDomainTagsByOrgId, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to load tags(s). Please try again.`)) - } - - if (tagsInfo.tags.length === 0) { - return { - edges: [], - totalCount: 0, - } - } - - const edges = tagsInfo.tags.map((tag) => { - return tag - }) - - return { - edges, - totalCount: tagsInfo.totalCount, - } - } diff --git a/api/src/domain/mutations/__tests__/add-organizations-domains.test.js b/api/src/domain/mutations/__tests__/add-organizations-domains.test.js index 6b1793081..bd0e262fb 100644 --- a/api/src/domain/mutations/__tests__/add-organizations-domains.test.js +++ b/api/src/domain/mutations/__tests__/add-organizations-domains.test.js @@ -1,5 +1,6 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as rawGraphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,14 +10,52 @@ import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, saltedHash, verifiedRequired, tfaRequired } from '../../../auth' -import { loadDomainByDomain } from '../../loaders' -import { loadOrgByKey } from '../../../organization/loaders' +import { DomainDataSource } from '../../data-source' +import { OrganizationDataSource } from '../../../organization/data-source' +import { AuditLogsDataSource } from '../../../audit-logs/data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url, HASHING_SECRET } = process.env +const withDataSources = (contextValue) => { + const query = contextValue?.query + const transaction = contextValue?.transaction + const collections = contextValue?.collections + const userKey = contextValue?.userKey + const i18n = contextValue?.i18n + const language = contextValue?.request?.language + const cleanseInput = contextValue?.validators?.cleanseInput + + const domainDataSource = + contextValue?.dataSources?.domain || new DomainDataSource({ query, userKey, i18n, transaction, collections }) + + const organizationDataSource = + contextValue?.dataSources?.organization || + new OrganizationDataSource({ query, userKey, i18n, language, cleanseInput, transaction, collections }) + + const auditLogs = + contextValue?.dataSources?.auditLogs || new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }) + + return { + ...contextValue, + dataSources: { + ...contextValue?.dataSources, + domain: domainDataSource, + organization: organizationDataSource, + auditLogs, + }, + } +} + +const graphql = ({ contextValue, ...args }) => { + return rawGraphql({ + ...args, + contextValue: withDataSources(contextValue), + }) +} + describe('given the addOrganizationsDomains mutation', () => { let query, drop, i18n, truncate, schema, collections, transaction, user, org @@ -120,7 +159,6 @@ describe('given the addOrganizationsDomains mutation', () => { input: { orgId: "${toGlobalId('organizations', org._key)}" domains: ["test.domain.gov", "test.domain2.gov"] - hideNewDomains: false tagNewDomains: false tagStagingDomains: false audit: false @@ -158,10 +196,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) @@ -190,7 +224,6 @@ describe('given the addOrganizationsDomains mutation', () => { input: { orgId: "${toGlobalId('organizations', org._key)}" domains: ["test.domain.gov", "test.domain2.gov"] - hideNewDomains: false tagNewDomains: false tagStagingDomains: false audit: true @@ -228,10 +261,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) @@ -337,7 +366,6 @@ describe('given the addOrganizationsDomains mutation', () => { input: { orgId: "${toGlobalId('organizations', org._key)}" domains: ["test.domain.gov", "test.domain2.gov"] - hideNewDomains: false tagNewDomains: false tagStagingDomains: false audit: false @@ -375,10 +403,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) diff --git a/api/src/domain/mutations/__tests__/create-domain.test.js b/api/src/domain/mutations/__tests__/create-domain.test.js index 5bc73c457..6eee9dde7 100644 --- a/api/src/domain/mutations/__tests__/create-domain.test.js +++ b/api/src/domain/mutations/__tests__/create-domain.test.js @@ -1,5 +1,6 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as rawGraphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -16,22 +17,91 @@ import { verifiedRequired, tfaRequired, checkDomainPermission, + AuthDataSource, } from '../../../auth' import { loadDkimSelectorsByDomainId, loadDomainByDomain } from '../../loaders' import { loadOrgByKey, loadOrgConnectionsByDomainId } from '../../../organization/loaders' +import { OrganizationDataSource } from '../../../organization/data-source' +import { DomainDataSource } from '../../data-source' +import { TagsDataSource } from '../../../tags/data-source' +import { AuditLogsDataSource } from '../../../audit-logs/data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url, HASHING_SECRET } = process.env +const withDataSources = (contextValue) => { + const query = contextValue?.query + const transaction = contextValue?.transaction + const collections = contextValue?.collections + const userKey = contextValue?.userKey + const i18n = contextValue?.i18n + const language = contextValue?.request?.language + const cleanseInput = contextValue?.validators?.cleanseInput + + const domainDataSource = + contextValue?.dataSources?.domain || new DomainDataSource({ query, userKey, i18n, transaction, collections }) + if (contextValue?.loaders?.loadDomainByDomain) { + domainDataSource.byDomain = contextValue.loaders.loadDomainByDomain + } + + const organizationDataSource = + contextValue?.dataSources?.organization || + new OrganizationDataSource({ query, userKey, i18n, language, cleanseInput, transaction, collections }) + if (contextValue?.loaders?.loadOrgByKey) { + organizationDataSource.byKey = contextValue.loaders.loadOrgByKey + } + + const tagsDataSource = + contextValue?.dataSources?.tags || new TagsDataSource({ query, userKey, i18n, language, transaction, collections }) + if (contextValue?.loaders?.loadTagByTagId) { + tagsDataSource.byTagId = contextValue.loaders.loadTagByTagId + } + + const auditLogs = + contextValue?.dataSources?.auditLogs || new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }) + + return { + ...contextValue, + dataSources: { + ...contextValue?.dataSources, + domain: domainDataSource, + organization: organizationDataSource, + tags: tagsDataSource, + auditLogs, + }, + } +} + +const graphql = ({ contextValue, ...args }) => { + return rawGraphql({ + ...args, + contextValue: withDataSources(contextValue), + }) +} + describe('create a domain', () => { - let query, drop, truncate, schema, collections, transaction, user, org + let query, drop, truncate, schema, collections, transaction, user, org, domain const consoleOutput = [] const mockedInfo = (output) => consoleOutput.push(output) const mockedWarn = (output) => consoleOutput.push(output) const mockedError = (output) => consoleOutput.push(output) + + const i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + beforeAll(async () => { console.info = mockedInfo console.warn = mockedWarn @@ -46,18 +116,6 @@ describe('create a domain', () => { consoleOutput.length = 0 }) describe('given a successful domain creation', () => { - const i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) beforeAll(async () => { ;({ query, drop, truncate, collections, transaction } = await ensure({ variables: { @@ -72,6 +130,24 @@ describe('create a domain', () => { })) }) beforeEach(async () => { + domain = ( + await collections.domains.save( + { + domain: 'test.gc.ca', + lastRan: '2021-01-01 12:00:00.000000', + selectors: [], + status: { + dkim: 'info', + dmarc: 'info', + https: 'info', + spf: 'info', + ssl: 'info', + }, + }, + { returnNew: true }, + ) + ).new + user = await collections.users.save({ userName: 'test.account@istio.actually.exists', emailValidated: true, @@ -126,7 +202,7 @@ describe('create a domain', () => { input: { orgId: "${toGlobalId('organization', org._key)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -185,6 +261,19 @@ describe('create a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), + }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ query, @@ -208,25 +297,20 @@ describe('create a domain', () => { }, }) - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() const expectedResponse = { data: { createDomain: { result: { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', - lastRan: null, - selectors: ['selector1', 'selector2'], + lastRan: '2021-01-01 12:00:00.000000', + selectors: [], status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, + dkim: 'INFO', + dmarc: 'INFO', + https: 'INFO', + spf: 'INFO', + ssl: 'INFO', }, organizations: { edges: [ @@ -292,7 +376,7 @@ describe('create a domain', () => { input: { orgId: "${toGlobalId('organization', org._key)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -351,6 +435,19 @@ describe('create a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), + }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ query, @@ -374,26 +471,20 @@ describe('create a domain', () => { }, }) - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() - const expectedResponse = { data: { createDomain: { result: { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', - lastRan: null, - selectors: ['selector1', 'selector2'], + lastRan: '2021-01-01 12:00:00.000000', + selectors: [], status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, + dkim: 'INFO', + dmarc: 'INFO', + https: 'INFO', + spf: 'INFO', + ssl: 'INFO', }, organizations: { edges: [ @@ -435,7 +526,7 @@ describe('create a domain', () => { input: { orgId: "${toGlobalId('organization', org._key)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -494,6 +585,19 @@ describe('create a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), + }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ query, @@ -517,26 +621,20 @@ describe('create a domain', () => { }, }) - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() - const expectedResponse = { data: { createDomain: { result: { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', - lastRan: null, - selectors: ['selector1', 'selector2'], + lastRan: '2021-01-01 12:00:00.000000', + selectors: [], status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, + dkim: 'INFO', + dmarc: 'INFO', + https: 'INFO', + spf: 'INFO', + ssl: 'INFO', }, organizations: { edges: [ @@ -593,43 +691,32 @@ describe('create a domain', () => { permission: 'super_admin', }) }) - describe('selectors are not added', () => { - beforeEach(async () => { - const domain = await collections.domains.save({ - domain: 'test.gc.ca', - lastRan: null, - status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, - }, - }) - const selector1 = await collections.selectors.save({ selector: 'selector1' }) - const selector2 = await collections.selectors.save({ selector: 'selector2' }) - await collections.domainsToSelectors.save({ - _from: domain._id, - _to: selector1._id, - }) - await collections.domainsToSelectors.save({ - _from: domain._id, - _to: selector2._id, - }) - await collections.claims.save({ - _from: org._id, - _to: domain._id, - }) + beforeEach(async () => { + const selector1 = await collections.selectors.save({ selector: 'selector1' }) + const selector2 = await collections.selectors.save({ selector: 'selector2' }) + await collections.domainsToSelectors.save({ + _from: domain._id, + _to: selector1._id, }) - it('returns the domain', async () => { - const response = await graphql({ - schema, - source: ` + await collections.domainsToSelectors.save({ + _from: domain._id, + _to: selector2._id, + }) + await collections.claims.save({ + _from: org._id, + _to: domain._id, + }) + }) + it('returns the domain', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', secondOrg._key)}" domain: "test.gc.ca" + assetState: APPROVED } ) { result { @@ -662,118 +749,112 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - saltedHash: saltedHash(HASHING_SECRET), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - checkSuperAdmin: checkSuperAdmin({ userKey: user._key, query }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadOrgConnectionsByDomainId: loadOrgConnectionsByDomainId({ - query, - language: 'en', - userKey: user._key, - cleanseInput, - auth: { loginRequired: true }, - }), + rootValue: null, + contextValue: { + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: user._key, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ + i18n, + userKey: user._key, + query, + }), + checkPermission: checkPermission({ userKey: user._key, query }), + saltedHash: saltedHash(HASHING_SECRET), + userRequired: userRequired({ + userKey: user._key, loadUserByKey: loadUserByKey({ query }), - }, - validators: { cleanseInput, slugify }, + }), + checkSuperAdmin: checkSuperAdmin({ userKey: user._key, query }), + verifiedRequired: verifiedRequired({}), + tfaRequired: tfaRequired({}), }, - }) - - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: loadDomainByDomain({ query }), + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + loadOrgConnectionsByDomainId: loadOrgConnectionsByDomainId({ + query, + language: 'en', + userKey: user._key, + cleanseInput, + auth: { loginRequired: true }, + }), + loadUserByKey: loadUserByKey({ query }), + }, + validators: { cleanseInput, slugify }, + }, + }) - const expectedResponse = { - data: { - createDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.gc.ca', - lastRan: null, - selectors: ['selector1', 'selector2'], - status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, - }, - organizations: { - edges: [ - { - node: { - id: toGlobalId('organization', org._key), - name: 'Treasury Board of Canada Secretariat', - }, + const expectedResponse = { + data: { + createDomain: { + result: { + id: toGlobalId('domain', domain._key), + domain: 'test.gc.ca', + lastRan: '2021-01-01 12:00:00.000000', + selectors: ['selector1', 'selector2'], + status: { + dkim: 'INFO', + dmarc: 'INFO', + https: 'INFO', + spf: 'INFO', + ssl: 'INFO', + }, + organizations: { + edges: [ + { + node: { + id: toGlobalId('organization', org._key), + name: 'Treasury Board of Canada Secretariat', }, - { - node: { - id: toGlobalId('organization', secondOrg._key), - name: 'Communications Security Establishment', - }, + }, + { + node: { + id: toGlobalId('organization', secondOrg._key), + name: 'Communications Security Establishment', }, - ], - }, + }, + ], }, }, }, - } + }, + } - expect(response).toEqual(expectedResponse) + expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully created ${domain.domain} in org: communications-security-establishment.`, - ]) - }) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully created ${domain.domain} in org: communications-security-establishment.`, + ]) }) - describe('selectors are the same', () => { + describe('lastRan is not changed', () => { beforeEach(async () => { - const domain = await collections.domains.save({ - domain: 'test.gc.ca', - selectors: ['selector1', 'selector2'], - lastRan: null, - status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, - }, - }) await collections.claims.save({ _from: org._id, _to: domain._id, @@ -788,7 +869,7 @@ describe('create a domain', () => { input: { orgId: "${toGlobalId('organization', secondOrg._key)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -847,6 +928,19 @@ describe('create a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), + }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ query, @@ -870,26 +964,20 @@ describe('create a domain', () => { }, }) - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() - const expectedResponse = { data: { createDomain: { result: { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', - lastRan: null, + lastRan: '2021-01-01 12:00:00.000000', selectors: ['selector1', 'selector2'], status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, + dkim: 'INFO', + dmarc: 'INFO', + https: 'INFO', + spf: 'INFO', + ssl: 'INFO', }, organizations: { edges: [ @@ -919,30 +1007,8 @@ describe('create a domain', () => { ]) }) }) - describe('new selectors are added', () => { + describe('status do not changed', () => { beforeEach(async () => { - const domain = await collections.domains.save({ - domain: 'test.gc.ca', - selectors: ['selector1', 'selector2'], - lastRan: null, - status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, - }, - }) - const selector1 = await collections.selectors.save({ selector: 'selector1' }) - const selector2 = await collections.selectors.save({ selector: 'selector2' }) - await collections.domainsToSelectors.save({ - _from: domain._id, - _to: selector1._id, - }) - await collections.domainsToSelectors.save({ - _from: domain._id, - _to: selector2._id, - }) await collections.claims.save({ _from: org._id, _to: domain._id, @@ -957,7 +1023,7 @@ describe('create a domain', () => { input: { orgId: "${toGlobalId('organization', secondOrg._key)}" domain: "test.gc.ca" - selectors: ["selector3", "selector4"] + assetState: APPROVED } ) { result { @@ -1016,6 +1082,19 @@ describe('create a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), + }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ query, @@ -1039,1592 +1118,75 @@ describe('create a domain', () => { }, }) - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() - const expectedResponse = { data: { createDomain: { result: { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', - lastRan: null, - selectors: ['selector1', 'selector2', 'selector3', 'selector4'], - status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, - }, - organizations: { - edges: [ - { - node: { - id: toGlobalId('organization', org._key), - name: 'Treasury Board of Canada Secretariat', - }, - }, - { - node: { - id: toGlobalId('organization', secondOrg._key), - name: 'Communications Security Establishment', - }, - }, - ], - }, - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully created ${domain.domain} in org: communications-security-establishment.`, - ]) - }) - }) - describe('lastRan is not changed', () => { - beforeEach(async () => { - const domain = await collections.domains.save({ - domain: 'test.gc.ca', - selectors: ['selector1', 'selector2'], - lastRan: '2021-01-01 12:00:00.000000', - status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, - }, - }) - await collections.claims.save({ - _from: org._id, - _to: domain._id, - }) - }) - it('returns the domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', secondOrg._key)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - saltedHash: saltedHash(HASHING_SECRET), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - checkSuperAdmin: checkSuperAdmin({ userKey: user._key, query }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadOrgConnectionsByDomainId: loadOrgConnectionsByDomainId({ - query, - language: 'en', - userKey: user._key, - cleanseInput, - auth: { loginRequired: true }, - }), - loadUserByKey: loadUserByKey({ query }), - }, - validators: { cleanseInput, slugify }, - }, - }) - - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() - - const expectedResponse = { - data: { - createDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.gc.ca', - lastRan: '2021-01-01 12:00:00.000000', - selectors: ['selector1', 'selector2'], - status: { - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, - }, - organizations: { - edges: [ - { - node: { - id: toGlobalId('organization', org._key), - name: 'Treasury Board of Canada Secretariat', - }, - }, - { - node: { - id: toGlobalId('organization', secondOrg._key), - name: 'Communications Security Establishment', - }, - }, - ], - }, - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully created ${domain.domain} in org: communications-security-establishment.`, - ]) - }) - }) - describe('status do not changed', () => { - beforeEach(async () => { - const domain = await collections.domains.save({ - domain: 'test.gc.ca', - selectors: ['selector1', 'selector2'], - lastRan: '', - status: { - dkim: 'fail', - dmarc: 'fail', - https: 'fail', - spf: 'fail', - ssl: 'fail', - }, - }) - await collections.claims.save({ - _from: org._id, - _to: domain._id, - }) - }) - it('returns the domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', secondOrg._key)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: user._key, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - saltedHash: saltedHash(HASHING_SECRET), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - checkSuperAdmin: checkSuperAdmin({ userKey: user._key, query }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadOrgConnectionsByDomainId: loadOrgConnectionsByDomainId({ - query, - language: 'en', - userKey: user._key, - cleanseInput, - auth: { loginRequired: true }, - }), - loadUserByKey: loadUserByKey({ query }), - }, - validators: { cleanseInput, slugify }, - }, - }) - - const domainCursor = await query` - FOR domain IN domains - RETURN domain - ` - const domain = await domainCursor.next() - - const expectedResponse = { - data: { - createDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.gc.ca', - lastRan: '', - selectors: ['selector1', 'selector2'], - status: { - dkim: 'FAIL', - dmarc: 'FAIL', - https: 'FAIL', - spf: 'FAIL', - ssl: 'FAIL', - }, - organizations: { - edges: [ - { - node: { - id: toGlobalId('organization', org._key), - name: 'Treasury Board of Canada Secretariat', - }, - }, - { - node: { - id: toGlobalId('organization', secondOrg._key), - name: 'Communications Security Establishment', - }, - }, - ], - }, - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - - expect(consoleOutput).toEqual([ - `User: ${user._key} successfully created ${domain.domain} in org: communications-security-establishment.`, - ]) - }) - }) - }) - }) - describe('given an unsuccessful domain creation', () => { - let i18n - describe('request language is english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('org does not exist', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { orgId: "b3JnYW5pemF0aW9uOjE=", domain: "test.gc.ca", selectors: ["selector1", "selector2"] } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn(), - saltedHash: jest.fn(), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = { - data: { - createDomain: { - result: { - code: 400, - description: 'Unable to create domain in unknown organization.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to create a domain to an organization: 1 that does not exist.`, - ]) - }) - }) - describe('user does not belong to organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue(undefined), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = { - data: { - createDomain: { - result: { - code: 400, - description: 'Permission Denied: Please contact organization user for help with creating domain.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to create a domain in: treasury-board-secretariat, however they do not have permission to do so.`, - ]) - }) - }) - describe('the domain already exists in the given organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({}), - }), - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = { - data: { - createDomain: { - result: { - code: 400, - description: 'Unable to create domain, organization has already claimed it.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to create a domain for: treasury-board-secretariat, however that org already has that domain claimed.`, - ]) - }) - }) - describe('database error occurs', () => { - describe('when checking to see if org already contains domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = [new GraphQLError('Unable to create domain. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred while running check to see if domain already exists in an org: Error: Database error occurred.`, - ]) - }) - }) - }) - describe('cursor error occurs', () => { - describe('when checking to see if org already contains domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockRejectedValue(new Error('Cursor error occurred.')), - }), - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = [new GraphQLError('Unable to create domain. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred while running check to see if domain already exists in an org: Error: Cursor error occurred.`, - ]) - }) - }) - describe('when gathering inserted domain', () => { - it('throws an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValueOnce({ - next: jest.fn().mockRejectedValue(new Error('cursor error')), - }), - }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = [new GraphQLError('Unable to create domain. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred for user: 123 when inserting new domain: Error: cursor error`, - ]) - }) - }) - }) - describe('transaction step error occurs', () => { - describe('when creating a new domain', () => { - describe('when inserting new domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = [new GraphQLError('Unable to create domain. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain: Error: trx step error`, - ]) - }) - }) - describe('when inserting new edge', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ - next: jest.fn(), - }) - .mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = [new GraphQLError('Unable to create domain. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain edge: Error: trx step error`, - ]) - }) - }) - }) - describe('when domain already exists', () => { - describe('when upserting domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValueOnce(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ - domain: 'domain.ca', - selectors: [], - status: {}, - lastRan: '', - }), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = [new GraphQLError('Unable to create domain. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain: Error: trx step error`, - ]) - }) - }) - describe('when inserting edge to new org', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValueOnce(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ next: jest.fn().mockReturnValue() }) - .mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: 123, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: 123, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ - domain: 'domain.ca', - selectors: [], - status: {}, - lastRan: '', - }), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, - }, - }) - - const error = [new GraphQLError('Unable to create domain. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain edge: Error: trx step error`, - ]) - }) - }) - }) - }) - describe('transaction commit error occurs', () => { - describe('when committing transaction', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createDomain( - input: { - orgId: "${toGlobalId('organization', 123)}" - domain: "test.gc.ca" - selectors: ["selector1", "selector2"] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - status { - dkim - dmarc - https - spf - ssl - } - organizations(first: 5) { - edges { - node { - id - name - } - } - } - } - ... on DomainError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({ next: jest.fn().mockReturnValue() }), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: 123, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: 123, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ - domain: 'domain.ca', - selectors: [], - status: {}, - lastRan: '', - }), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), + lastRan: '2021-01-01 12:00:00.000000', + selectors: ['selector1', 'selector2'], + status: { + dkim: 'INFO', + dmarc: 'INFO', + https: 'INFO', + spf: 'INFO', + ssl: 'INFO', }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), + organizations: { + edges: [ + { + node: { + id: toGlobalId('organization', org._key), + name: 'Treasury Board of Canada Secretariat', + }, + }, + { + node: { + id: toGlobalId('organization', secondOrg._key), + name: 'Communications Security Establishment', + }, + }, + ], }, }, - validators: { cleanseInput, slugify }, }, - }) + }, + } - const error = [new GraphQLError('Unable to create domain. Please try again.')] + expect(response).toEqual(expectedResponse) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction commit error occurred while user: 123 was creating domain: Error: trx commit error`, - ]) - }) + expect(consoleOutput).toEqual([ + `User: ${user._key} successfully created ${domain.domain} in org: communications-security-establishment.`, + ]) }) }) }) - describe('request language is french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) + }) + describe('given an unsuccessful domain creation', () => { + let i18n + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, }) - describe('org does not exist', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` + }) + describe('org does not exist', () => { + it('returns an error', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( - input: { orgId: "b3JnYW5pemF0aW9uOjE=", domain: "test.gc.ca", selectors: ["selector1", "selector2"] } + input: { orgId: "b3JnYW5pemF0aW9uOjE=", domain: "test.gc.ca", assetState: APPROVED, cvdEnrollment: { status: ENROLLED } } ) { result { ... on Domain { @@ -2639,6 +1201,9 @@ describe('create a domain', () => { spf ssl } + cvdEnrollment { + status + } organizations(first: 5) { edges { node { @@ -2656,80 +1221,155 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ + i18n, + userKey: user._key, + query, + }), + checkPermission: jest.fn(), + saltedHash: jest.fn(), + userRequired: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), }, - query, - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn(), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), + loadOrgByKey: { + load: jest.fn().mockReturnValue(undefined), }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), }, - validators: { cleanseInput, slugify }, }, - }) + validators: { cleanseInput, slugify }, + }, + }) - const error = { - data: { - createDomain: { - result: { - code: 400, - description: 'Impossible de créer un domaine dans une organisation inconnue.', - }, + const error = { + data: { + createDomain: { + result: { + code: 400, + description: 'Unable to create domain in unknown organization.', }, }, - } + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to create a domain to an organization: 1 that does not exist.`, - ]) + expect(response).toEqual(error) + expect(consoleOutput).toEqual([ + `User: 123 attempted to create a domain to an organization: 1 that does not exist.`, + ]) + }) + + it('returns the domain with cvdEnrollment status not-enrolled when not provided', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + createDomain( + input: { orgId: "b3JnYW5pemF0aW9uOjE=", domain: "test.gc.ca", assetState: APPROVED } + ) { + result { + ... on Domain { + cvdEnrollment { + status + } + } + } + } + } + `, + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ + i18n, + userKey: user._key, + query, + }), + checkPermission: jest.fn(), + saltedHash: jest.fn(), + userRequired: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue(undefined), + }, + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, }) + // Should default to not-enrolled + if ( + response.data && + response.data.createDomain && + response.data.createDomain.result && + response.data.createDomain.result.cvdEnrollment + ) { + expect(response.data.createDomain.result.cvdEnrollment.status).toBe('not-enrolled') + } }) - describe('user does not belong to organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` + }) + }) + describe('user does not belong to organization', () => { + it('returns an error', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -2762,83 +1402,82 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query, + collections: collectionNames, + transaction, + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, + userKey: user._key, query, - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue(undefined), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, + }), + checkPermission: jest.fn().mockReturnValue(undefined), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), }, - }) + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', + }), + }, + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = { - data: { - createDomain: { - result: { - code: 400, - description: - "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la création du domaine.", - }, - }, + const error = { + data: { + createDomain: { + result: { + code: 403, + description: 'Permission Denied: Please contact organization user for help with creating domain.', }, - } + }, + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to create a domain in: treasury-board-secretariat, however they do not have permission to do so.`, - ]) - }) - }) - describe('the domain already exists in the given organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` + expect(response).toEqual(error) + expect(consoleOutput).toEqual([ + `User: 123 attempted to create a domain in: treasury-board-secretariat, however they do not have permission to do so.`, + ]) + }) + }) + describe('the domain already exists in the given organization', () => { + it('returns an error', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -2871,85 +1510,85 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockReturnValue({}), + }), + collections: collectionNames, + transaction, + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({}), + userKey: user._key, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = { - data: { - createDomain: { - result: { - code: 400, - description: "Impossible de créer le domaine, l'organisation l'a déjà réclamé.", - }, - }, + const error = { + data: { + createDomain: { + result: { + code: 400, + description: 'Unable to create domain, organization has already claimed it.', }, - } + }, + }, + } - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to create a domain for: treasury-board-secretariat, however that org already has that domain claimed.`, - ]) - }) - }) - describe('database error occurs', () => { - describe('when checking to see if org already contains domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + expect(response).toEqual(error) + expect(consoleOutput).toEqual([ + `User: 123 attempted to create a domain for: treasury-board-secretariat, however that org already has that domain claimed.`, + ]) + }) + }) + describe('database error occurs', () => { + describe('when checking to see if org already contains domain', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -2982,75 +1621,75 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), + collections: collectionNames, + transaction, + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, + userKey: user._key, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), }, - }) + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', + }), + }, + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred while running check to see if domain already exists in an org: Error: Database error occurred.`, - ]) - }) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Database error occurred while running check to see if domain already exists in an org: Error: Database error occurred.`, + ]) }) - describe('cursor error occurs', () => { - describe('when checking to see if org already contains domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + }) + }) + describe('cursor error occurs', () => { + describe('when checking to see if org already contains domain', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -3083,75 +1722,75 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockRejectedValue(new Error('Cursor error occurred.')), + }), + collections: collectionNames, + transaction, + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockRejectedValue(new Error('Cursor error occurred.')), + userKey: user._key, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - collections: collectionNames, - transaction, - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred while running check to see if domain already exists in an org: Error: Cursor error occurred.`, - ]) - }) - }) - describe('when gathering inserted domain', () => { - it('throws an error', async () => { - const response = await graphql({ - schema, - source: ` + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Cursor error occurred while running check to see if domain already exists in an org: Error: Cursor error occurred.`, + ]) + }) + }) + describe('when gathering inserted domain', () => { + it('throws an error', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -3184,82 +1823,83 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockReturnValue(undefined), + }), + collections: collectionNames, + transaction: jest.fn().mockReturnValue({ + step: jest.fn().mockReturnValueOnce({ + next: jest.fn().mockRejectedValue(new Error('cursor error')), + }), + abort: jest.fn(), + }), + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValueOnce({ - next: jest.fn().mockRejectedValue(new Error('cursor error')), - }), + userKey: user._key, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: 123, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: 123, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred for user: 123 when inserting new domain: Error: cursor error`, - ]) - }) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Cursor error occurred for user: 123 when inserting new domain: Error: cursor error`, + ]) }) - describe('transaction step error occurs', () => { - describe('when creating a new domain', () => { - describe('when inserting new domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + }) + }) + describe('transaction step error occurs', () => { + describe('when creating a new domain', () => { + describe('when inserting new domain', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -3292,77 +1932,78 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockReturnValue(undefined), + }), + collections: collectionNames, + transaction: jest.fn().mockReturnValue({ + step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), + }), + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), + userKey: user._key, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain: Error: trx step error`, - ]) - }) - }) - describe('when inserting new edge', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Transaction step error occurred for user: 123 when inserting new domain: Error: trx step error`, + ]) + }) + }) + describe('when inserting new edge', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -3395,84 +2036,85 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockReturnValue(undefined), + }), + collections: collectionNames, + transaction: jest.fn().mockReturnValue({ + step: jest + .fn() + .mockReturnValueOnce({ + next: jest.fn(), + }) + .mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), + }), + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue(undefined), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ - next: jest.fn(), - }) - .mockRejectedValue(new Error('trx step error')), + userKey: user._key, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn(), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn(), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain edge: Error: trx step error`, - ]) - }) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Transaction step error occurred for user: 123 when inserting new domain edge: Error: trx step error`, + ]) }) - describe('when domain already exists', () => { - describe('when upserting domain', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + }) + }) + describe('when domain already exists', () => { + describe('when upserting domain', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -3505,82 +2147,83 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockReturnValueOnce(undefined), + }), + collections: collectionNames, + transaction: jest.fn().mockReturnValue({ + step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), + }), + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValueOnce(undefined), + userKey: user._key, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: user._key, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn().mockReturnValue({ + domain: 'domain.ca', + selectors: [], + status: {}, + lastRan: '', }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ - domain: 'domain.ca', - selectors: [], - status: {}, - lastRan: '', - }), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain: Error: trx step error`, - ]) - }) - }) - describe('when inserting edge to new org', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Transaction step error occurred for user: 123 when inserting new domain: Error: trx step error`, + ]) + }) + }) + describe('when inserting edge to new org', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -3613,88 +2256,89 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockReturnValueOnce(undefined), + }), + collections: collectionNames, + transaction: jest.fn().mockReturnValue({ + step: jest + .fn() + .mockReturnValueOnce({ next: jest.fn().mockReturnValue() }) + .mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), + }), + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValueOnce(undefined), + userKey: 123, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: 123, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn().mockReturnValue({ + domain: 'domain.ca', + selectors: [], + status: {}, + lastRan: '', }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ next: jest.fn().mockReturnValueOnce(undefined) }) - .mockRejectedValue(new Error('trx step error')), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: 123, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: 123, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ - domain: 'domain.ca', - selectors: [], - status: {}, - lastRan: '', - }), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction step error occurred for user: 123 when inserting new domain edge: Error: trx step error`, - ]) - }) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Transaction step error occurred for user: 123 when inserting new domain edge: Error: trx step error`, + ]) }) }) - describe('transaction commit error occurs', () => { - describe('when committing transaction', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` + }) + }) + describe('transaction commit error occurs', () => { + describe('when committing transaction', () => { + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` mutation { createDomain( input: { orgId: "${toGlobalId('organization', 123)}" domain: "test.gc.ca" - selectors: ["selector1", "selector2"] + assetState: APPROVED } ) { result { @@ -3727,72 +2371,71 @@ describe('create a domain', () => { } } `, - rootValue: null, - contextValue: { + rootValue: null, + contextValue: { + i18n, + request: { + language: 'en', + }, + query: jest.fn().mockReturnValue({ + next: jest.fn().mockReturnValue(undefined), + }), + collections: collectionNames, + transaction: jest.fn().mockReturnValue({ + step: jest.fn().mockReturnValue({ next: jest.fn().mockReturnValue() }), + commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), + }), + userKey: 123, + publish: jest.fn(), + auth: { + checkDomainPermission: checkDomainPermission({ i18n, - request: { - language: 'en', - }, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValueOnce(undefined), + userKey: 123, + query, + }), + checkPermission: jest.fn().mockReturnValue('admin'), + userRequired: jest.fn(), + saltedHash: jest.fn(), + verifiedRequired: jest.fn(), + tfaRequired: jest.fn(), + }, + loaders: { + loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + query, + userKey: 123, + cleanseInput, + i18n, + auth: { loginRequiredBool: true }, + }), + loadDomainByDomain: { + load: jest.fn().mockReturnValue({ + domain: 'domain.ca', + selectors: [], + status: {}, + lastRan: '', }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({ next: jest.fn().mockReturnValueOnce(undefined) }), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + }, + loadOrgByKey: { + load: jest.fn().mockReturnValue({ + slug: 'treasury-board-secretariat', }), - userKey: 123, - publish: jest.fn(), - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: 123, - query, - }), - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn(), - saltedHash: jest.fn(), - verifiedRequired: jest.fn(), - tfaRequired: jest.fn(), - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: 123, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ - domain: 'domain.ca', - selectors: [], - status: {}, - lastRan: '', - }), - }, - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - slug: 'treasury-board-secretariat', - }), - }, - loadOrgConnectionsByDomainId: jest.fn(), - loadUserByKey: { - load: jest.fn(), - }, - }, - validators: { cleanseInput, slugify }, }, - }) + loadOrgConnectionsByDomainId: jest.fn(), + loadUserByKey: { + load: jest.fn(), + }, + }, + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer un domaine. Veuillez réessayer.')] + const error = [new GraphQLError('Unable to create domain. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction commit error occurred while user: 123 was creating domain: Error: trx commit error`, - ]) - }) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([ + `Transaction commit error occurred while user: 123 was creating domain: Error: trx commit error`, + ]) }) }) }) diff --git a/api/src/domain/mutations/__tests__/favourite-domain.test.js b/api/src/domain/mutations/__tests__/favourite-domain.test.js index 0dbf09276..b6677b5f4 100644 --- a/api/src/domain/mutations/__tests__/favourite-domain.test.js +++ b/api/src/domain/mutations/__tests__/favourite-domain.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -6,12 +7,12 @@ import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { userRequired, verifiedRequired } from '../../../auth' -import { loadDomainByKey } from '../../loaders' +import { DomainDataSource } from '../../data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' -const { DB_PASS: rootPass, DB_URL: url, HASHING_SECRET } = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('favourite a domain', () => { let query, drop, truncate, schema, collections, transaction, user, domain1 @@ -99,10 +100,12 @@ describe('favourite a domain', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadDomainByKey: loadDomainByKey({ + dataSources: { + domain: new DomainDataSource({ query, userKey: user._key, + transaction, + collections: collectionNames, }), }, validators: { cleanseInput }, diff --git a/api/src/domain/mutations/__tests__/ignore-cve.test.js b/api/src/domain/mutations/__tests__/ignore-cve.test.js new file mode 100644 index 000000000..6c93eb059 --- /dev/null +++ b/api/src/domain/mutations/__tests__/ignore-cve.test.js @@ -0,0 +1,351 @@ +import { graphql, GraphQLError, GraphQLSchema } from 'graphql' +import { toGlobalId } from 'graphql-relay' +import { createMutationSchema } from '../../../mutation' +import { createUserContextGenerator, ensureDatabase as ensure } from '../../../testUtilities' +import { dbNameFromFile } from 'arango-tools' +import dbschema from '../../../../../database-migration/database.json' +import { createQuerySchema } from '../../../query' +import { createI18n } from '../../../create-i18n' +import { collectionNames } from '../../../collection-names' + +const { DB_PASS: rootPass, DB_URL: url, AUTHENTICATED_KEY, HASHING_SALT } = process.env + +const schema = new GraphQLSchema({ + query: createQuerySchema(), + mutation: createMutationSchema(), +}) +const consoleOutput = [] +const mockedInfo = (output) => consoleOutput.push(output) +const mockedWarn = (output) => consoleOutput.push(output) +const mockedError = (output) => consoleOutput.push(output) +console.info = mockedInfo +console.warn = mockedWarn +console.error = mockedError + +const i18n = createI18n('en') + +let db, + query, + drop, + truncate, + collections, + transaction, + createUserContext, + domain, + superAdminUser, + superAdminContext, + normalUser, + normalUserContext + +describe('ignoreCve mutation', () => { + beforeAll(async () => { + ;({ db, query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + + createUserContext = createUserContextGenerator({ + db, + query, + transaction, + collectionNames, + i18n, + secret: AUTHENTICATED_KEY, + salt: HASHING_SALT, + }) + }) + + beforeEach(async () => { + superAdminUser = ( + await collections.users.save( + { + _key: 'superadminuser', + userName: 'sueradminuser@test.gc.ca', + emailValidated: true, + }, + { returnNew: true }, + ) + ).new + superAdminContext = await createUserContext({ userKey: superAdminUser._key }) + normalUser = ( + await collections.users.save( + { + _key: 'normaluser', + userName: 'normaluser@test.gc.ca', + emailValidated: true, + }, + { returnNew: true }, + ) + ).new + normalUserContext = await createUserContext({ userKey: normalUser._key }) + + const superAdminOrg = ( + await collections.organizations.save( + { + _key: 'superadminorg', + orgDetails: { + en: { + slug: 'super-admin', + acronym: 'SA', + }, + fr: { + slug: 'super-admin', + acronym: 'SA', + }, + }, + }, + { returnNew: true }, + ) + ).new + await collections.affiliations.save( + { + _from: superAdminOrg._id, + _to: superAdminUser._id, + permission: 'super_admin', + }, + { returnNew: true }, + ) + + domain = ( + await collections.domains.save( + { + _key: '123', + domain: 'test.domain.gc.ca', + slug: 'test-domain-gc-ca', + }, + { returnNew: true }, + ) + ).new + }) + + afterEach(async () => { + consoleOutput.length = 0 + await truncate() + }) + + afterAll(async () => { + await drop() + }) + + it('returns an error when the user is not a super admin', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + ignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "CVE-1234-55555" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: normalUserContext, + }) + + const expectedError = 'Permissions error. You do not have sufficient permissions to access this data.' + expect(response.errors[0].message).toEqual(expectedError) + + const expectConsoleOutput = [ + `User: ${normalUser._key} attempted to access controlled functionality without sufficient privileges.`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + describe('given a super admin user', () => { + it('returns an error when the domain does not exist', async () => { + const badDomainKey = 'bad-domain-key' + const cve = 'CVE-1234-55555' + const response = await graphql({ + schema, + source: ` + mutation { + ignoreCve(input: { domainId: "${toGlobalId('domain', badDomainKey)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const expectedError = { + code: 400, + description: 'Unable to ignore CVE. Please try again.', + } + expect(response.data.ignoreCve.result).toEqual(expectedError) + + const expectConsoleOutput = [ + `User: "${superAdminUser._key}" attempted to ignore CVE "${cve}" on unknown domain: "${badDomainKey}".`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('returns an error when the CVE is already ignored', async () => { + const cve = 'CVE-1234-55555' + // Add the CVE to the domain's ignoredCves array + await query` + UPDATE { + _key: ${domain._key}, + ignoredCves: ${[cve]} + } IN domains + ` + const response = await graphql({ + schema, + source: ` + mutation { + ignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const expectedError = { + code: 400, + description: 'CVE is already ignored for this domain.', + } + expect(response.data.ignoreCve.result).toEqual(expectedError) + + const expectConsoleOutput = [ + `User: "${superAdminUser._key}" attempted to ignore CVE "${cve}" on domain: "${domain._key}" however CVE is already ignored.`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('throws an error when the transaction step fails', async () => { + const cve = 'CVE-1234-55555' + superAdminContext.dataSources.domain._transaction = jest.fn().mockReturnValue({ + step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), + }) + + const response = await graphql({ + schema, + source: ` + mutation { + ignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const error = [new GraphQLError('Unable to ignore CVE. Please try again.')] + expect(response.errors).toEqual(error) + + const expectConsoleOutput = [ + `Transaction step error occurred when user: "${superAdminUser._key}" attempted to ignore CVE "${cve}" on domain "${domain._key}", error: Error: Transaction step error`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('throws an error when the transaction commit fails', async () => { + const cve = 'CVE-1234-55555' + superAdminContext.dataSources.domain._transaction = jest.fn().mockReturnValue({ + step: jest.fn().mockReturnValue(), + commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), + }) + + const response = await graphql({ + schema, + source: ` + mutation { + ignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const error = [new GraphQLError('Unable to ignore CVE. Please try again.')] + expect(response.errors).toEqual(error) + + const expectConsoleOutput = [ + `Transaction commit error occurred when user: "${superAdminUser._key}" attempted to ignore CVE "${cve}" on domain "${domain._key}", error: Error: Transaction commit error`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('successfully ignores a CVE', async () => { + // Ensure CSV is not already ignored + const currentDomainState = await (await query`RETURN DOCUMENT(domains, ${domain._key}).ignoredCves || []`).next() + expect(currentDomainState).toEqual([]) + + const cve = 'CVE-1234-55555' + const response = await graphql({ + schema, + source: ` + mutation { + ignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on Domain { + domain + ignoredCves + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const expectedResponse = { + data: { + ignoreCve: { + result: { + domain: domain.domain, + ignoredCves: [cve], + }, + }, + }, + } + expect(response).toEqual(expectedResponse) + + const domainCursor = await query` + FOR domain IN domains + FILTER domain._key == ${domain._key} + RETURN domain + ` + const domainArr = await domainCursor.all() + const domainObj = domainArr[0] + expect(domainObj.ignoredCves).toEqual([cve]) + + const expectConsoleOutput = [ + `User: "${superAdminUser._key}" successfully ignored CVE "${cve}" on domain: "${domain._key}".`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + }) +}) diff --git a/api/src/domain/mutations/__tests__/remove-domain.test.js b/api/src/domain/mutations/__tests__/remove-domain.test.js index 1f266358f..8b366d71f 100644 --- a/api/src/domain/mutations/__tests__/remove-domain.test.js +++ b/api/src/domain/mutations/__tests__/remove-domain.test.js @@ -1,6 +1,7 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as rawGraphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { createQuerySchema } from '../../../query' @@ -9,6 +10,9 @@ import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired, tfaRequired } from '../../../auth' +import { logActivity } from '../../../audit-logs' +import { DomainDataSource } from '../../data-source' +import { OrganizationDataSource } from '../../../organization/data-source' import { loadDomainByKey } from '../../loaders' import { loadOrgByKey } from '../../../organization/loaders' import { loadUserByKey } from '../../../user/loaders' @@ -17,6 +21,51 @@ import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url } = process.env +const withDataSources = (contextValue) => { + const query = contextValue?.query + const transaction = contextValue?.transaction + const collections = contextValue?.collections + const userKey = contextValue?.userKey + const i18n = contextValue?.i18n + const language = contextValue?.request?.language + const cleanseInput = contextValue?.validators?.cleanseInput + + const domainDataSource = + contextValue?.dataSources?.domain || + new DomainDataSource({ query, userKey, i18n, transaction, collections }) + if (contextValue?.loaders?.loadDomainByKey) { + domainDataSource.byKey = contextValue.loaders.loadDomainByKey + } + + const organizationDataSource = + contextValue?.dataSources?.organization || + new OrganizationDataSource({ query, userKey, i18n, language, cleanseInput, transaction, collections }) + if (contextValue?.loaders?.loadOrgByKey) { + organizationDataSource.byKey = contextValue.loaders.loadOrgByKey + } + + const auditLogs = contextValue?.dataSources?.auditLogs || { + logActivity: (payload) => logActivity({ query, transaction, collections, ...payload }), + } + + return { + ...contextValue, + dataSources: { + ...contextValue?.dataSources, + domain: domainDataSource, + organization: organizationDataSource, + auditLogs, + }, + } +} + +const graphql = ({ contextValue, ...args }) => { + return rawGraphql({ + ...args, + contextValue: withDataSources(contextValue), + }) +} + describe('removing a domain', () => { let schema, i18n, query, drop, truncate, collections, transaction, user @@ -225,6 +274,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -314,6 +364,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -527,6 +578,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -722,6 +774,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -811,6 +864,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1028,6 +1082,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1200,6 +1255,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1289,6 +1345,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1363,6 +1420,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1427,6 +1485,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1505,6 +1564,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1669,6 +1729,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1758,6 +1819,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1832,6 +1894,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1896,6 +1959,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1974,6 +2038,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2243,6 +2308,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2332,6 +2398,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2545,6 +2612,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2711,6 +2779,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2800,6 +2869,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2874,6 +2944,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2938,6 +3009,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -3012,6 +3084,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -3180,6 +3253,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -3249,6 +3323,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -3321,6 +3396,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -3396,6 +3472,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn(), @@ -3471,6 +3548,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn(), @@ -3548,6 +3626,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn(), @@ -3623,6 +3702,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn(), @@ -3700,6 +3780,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -3771,6 +3852,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -3811,6 +3893,7 @@ describe('removing a domain', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -3849,6 +3932,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -3886,6 +3970,7 @@ describe('removing a domain', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -3924,6 +4009,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -3966,6 +4052,7 @@ describe('removing a domain', () => { .mockReturnValueOnce() .mockReturnValueOnce() .mockRejectedValue(new Error('Transaction error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -4007,6 +4094,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4053,6 +4141,7 @@ describe('removing a domain', () => { .mockReturnValueOnce() .mockReturnValueOnce() .mockRejectedValue(new Error('Step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -4091,6 +4180,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4133,6 +4223,7 @@ describe('removing a domain', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -4168,6 +4259,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4207,6 +4299,7 @@ describe('removing a domain', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -4245,6 +4338,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4328,6 +4422,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4397,6 +4492,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4469,6 +4565,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4545,6 +4642,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn(), @@ -4621,6 +4719,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn(), @@ -4699,6 +4798,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn(), @@ -4775,6 +4875,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn(), @@ -4853,6 +4954,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4924,6 +5026,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -4963,6 +5066,7 @@ describe('removing a domain', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -5001,6 +5105,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -5037,6 +5142,7 @@ describe('removing a domain', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -5075,6 +5181,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -5116,6 +5223,7 @@ describe('removing a domain', () => { .mockReturnValueOnce() .mockReturnValueOnce() .mockRejectedValue(new Error('Transaction error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -5157,6 +5265,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -5202,6 +5311,7 @@ describe('removing a domain', () => { .mockReturnValueOnce() .mockReturnValueOnce() .mockRejectedValue(new Error('Step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -5240,6 +5350,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -5283,6 +5394,7 @@ describe('removing a domain', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -5318,6 +5430,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -5357,6 +5470,7 @@ describe('removing a domain', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -5395,6 +5509,7 @@ describe('removing a domain', () => { collections: collectionNames, transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), diff --git a/api/src/domain/mutations/__tests__/remove-organizations-domains.test.js b/api/src/domain/mutations/__tests__/remove-organizations-domains.test.js index 74a69df36..f1f8a0041 100644 --- a/api/src/domain/mutations/__tests__/remove-organizations-domains.test.js +++ b/api/src/domain/mutations/__tests__/remove-organizations-domains.test.js @@ -1,5 +1,6 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as rawGraphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,14 +10,52 @@ import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired, tfaRequired } from '../../../auth' -import { loadDomainByDomain } from '../../loaders' -import { loadOrgByKey } from '../../../organization/loaders' +import { DomainDataSource } from '../../data-source' +import { OrganizationDataSource } from '../../../organization/data-source' +import { AuditLogsDataSource } from '../../../audit-logs/data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url } = process.env +const withDataSources = (contextValue) => { + const query = contextValue?.query + const transaction = contextValue?.transaction + const collections = contextValue?.collections + const userKey = contextValue?.userKey + const i18n = contextValue?.i18n + const language = contextValue?.request?.language + const cleanseInput = contextValue?.validators?.cleanseInput + + const domainDataSource = + contextValue?.dataSources?.domain || new DomainDataSource({ query, userKey, i18n, transaction, collections }) + + const organizationDataSource = + contextValue?.dataSources?.organization || + new OrganizationDataSource({ query, userKey, i18n, language, cleanseInput, transaction, collections }) + + const auditLogs = + contextValue?.dataSources?.auditLogs || new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }) + + return { + ...contextValue, + dataSources: { + ...contextValue?.dataSources, + domain: domainDataSource, + organization: organizationDataSource, + auditLogs, + }, + } +} + +const graphql = ({ contextValue, ...args }) => { + return rawGraphql({ + ...args, + contextValue: withDataSources(contextValue), + }) +} + describe('given the addOrganizationsDomains mutation', () => { let query, drop, i18n, truncate, schema, collections, transaction, user, org, domain, domain2, org2 @@ -232,10 +271,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) @@ -299,10 +334,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) @@ -367,10 +398,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) @@ -566,10 +593,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) @@ -632,10 +655,6 @@ describe('given the addOrganizationsDomains mutation', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - }, validators: { cleanseInput }, }, }) diff --git a/api/src/domain/mutations/__tests__/unfavourite-domain.test.js b/api/src/domain/mutations/__tests__/unfavourite-domain.test.js index 365525c13..b1dedb740 100644 --- a/api/src/domain/mutations/__tests__/unfavourite-domain.test.js +++ b/api/src/domain/mutations/__tests__/unfavourite-domain.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -9,12 +10,12 @@ import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { userRequired, verifiedRequired } from '../../../auth' -import { loadDomainByKey } from '../../loaders' +import { DomainDataSource } from '../../data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' -const { DB_PASS: rootPass, DB_URL: url, HASHING_SECRET } = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('favourite a domain', () => { let query, drop, i18n, truncate, schema, collections, transaction, user, domain1, favourite1 @@ -121,10 +122,13 @@ describe('favourite a domain', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadDomainByKey: loadDomainByKey({ + dataSources: { + domain: new DomainDataSource({ query, userKey: user._key, + i18n, + transaction, + collections: collectionNames, }), }, validators: { cleanseInput }, diff --git a/api/src/domain/mutations/__tests__/unignore-cve.test.js b/api/src/domain/mutations/__tests__/unignore-cve.test.js new file mode 100644 index 000000000..bdc9564f5 --- /dev/null +++ b/api/src/domain/mutations/__tests__/unignore-cve.test.js @@ -0,0 +1,353 @@ +import { graphql, GraphQLError, GraphQLSchema } from 'graphql' +import { toGlobalId } from 'graphql-relay' +import { createMutationSchema } from '../../../mutation' +import { ensureDatabase as ensure, createUserContextGenerator } from '../../../testUtilities' +import { dbNameFromFile } from 'arango-tools' +import dbschema from '../../../../../database-migration/database.json' +import { createQuerySchema } from '../../../query' +import { createI18n } from '../../../create-i18n' +import { collectionNames } from '../../../collection-names' + +const { DB_PASS: rootPass, DB_URL: url, AUTHENTICATED_KEY, HASHING_SALT } = process.env + +const schema = new GraphQLSchema({ + query: createQuerySchema(), + mutation: createMutationSchema(), +}) +const consoleOutput = [] +const mockedInfo = (output) => consoleOutput.push(output) +const mockedWarn = (output) => consoleOutput.push(output) +const mockedError = (output) => consoleOutput.push(output) +console.info = mockedInfo +console.warn = mockedWarn +console.error = mockedError + +const i18n = createI18n('en') + +let db, + query, + drop, + truncate, + collections, + transaction, + createUserContext, + domain, + superAdminUser, + superAdminContext, + normalUser, + normalUserContext + +const cve = 'CVE-1234-55555' + +describe('unignore mutation', () => { + beforeAll(async () => { + ;({ db, query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + + createUserContext = createUserContextGenerator({ + query, + db, + transaction, + collectionNames, + i18n, + secret: AUTHENTICATED_KEY, + salt: HASHING_SALT, + }) + }) + + beforeEach(async () => { + superAdminUser = ( + await collections.users.save( + { + _key: 'superadminuser', + userName: 'sueradminuser@test.gc.ca', + emailValidated: true, + }, + { returnNew: true }, + ) + ).new + superAdminContext = await createUserContext({ userKey: superAdminUser._key }) + normalUser = ( + await collections.users.save( + { + _key: 'normaluser', + userName: 'normaluser@test.gc.ca', + emailValidated: true, + }, + { returnNew: true }, + ) + ).new + normalUserContext = await createUserContext({ userKey: normalUser._key }) + + const superAdminOrg = ( + await collections.organizations.save( + { + _key: 'superadminorg', + orgDetails: { + en: { + slug: 'super-admin', + acronym: 'SA', + }, + fr: { + slug: 'super-admin', + acronym: 'SA', + }, + }, + }, + { returnNew: true }, + ) + ).new + await collections.affiliations.save( + { + _from: superAdminOrg._id, + _to: superAdminUser._id, + permission: 'super_admin', + }, + { returnNew: true }, + ) + + domain = ( + await collections.domains.save( + { + _key: '123', + domain: 'test.domain.gc.ca', + slug: 'test-domain-gc-ca', + ignoredCves: [cve], + }, + { returnNew: true }, + ) + ).new + }) + + afterEach(async () => { + consoleOutput.length = 0 + await truncate() + }) + + afterAll(async () => { + await drop() + }) + + it('returns an error when the user is not a super admin', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + unignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: normalUserContext, + }) + + const expectedError = 'Permissions error. You do not have sufficient permissions to access this data.' + expect(response.errors[0].message).toEqual(expectedError) + + const expectConsoleOutput = [ + `User: ${normalUser._key} attempted to access controlled functionality without sufficient privileges.`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + describe('given a super admin user', () => { + it('returns an error when the domain does not exist', async () => { + const badDomainKey = 'bad-domain-key' + const response = await graphql({ + schema, + source: ` + mutation { + unignoreCve(input: { domainId: "${toGlobalId('domain', badDomainKey)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const expectedError = { + code: 400, + description: 'Unable to stop ignoring CVE. Please try again.', + } + expect(response.data.unignoreCve.result).toEqual(expectedError) + + const expectConsoleOutput = [ + `User: "${superAdminUser._key}" attempted to unignore CVE "${cve}" on unknown domain: "${badDomainKey}".`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('returns an error when the CVE is not already ignored', async () => { + // Remove the CVE from the domain's ignoredCves + await query` + UPDATE { + _key: ${domain._key}, + ignoredCves: [] + } IN domains + ` + // Ensure the CVE is not already ignored + const currentDomainState = await (await query`RETURN DOCUMENT(domains, ${domain._key}).ignoredCves || []`).next() + expect(currentDomainState).toEqual([]) + + const response = await graphql({ + schema, + source: ` + mutation { + unignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const expectedError = { + code: 400, + description: 'CVE is not ignored for this domain.', + } + expect(response.data.unignoreCve.result).toEqual(expectedError) + + const expectConsoleOutput = [ + `User: "${superAdminUser._key}" attempted to unignore CVE "${cve}" on domain: "${domain._key}" however CVE is not ignored.`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('throws an error when the transaction step fails', async () => { + superAdminContext.dataSources.domain._transaction = jest.fn().mockReturnValue({ + step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), + }) + + const response = await graphql({ + schema, + source: ` + mutation { + unignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const error = [new GraphQLError('Unable to stop ignoring CVE. Please try again.')] + expect(response.errors).toEqual(error) + + const expectConsoleOutput = [ + `Transaction step error occurred when user: "${superAdminUser._key}" attempted to unignore CVE "${cve}" on domain "${domain._key}", error: Error: Transaction step error`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('throws an error when the transaction commit fails', async () => { + superAdminContext.dataSources.domain._transaction = jest.fn().mockReturnValue({ + step: jest.fn().mockReturnValue(), + commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), + }) + + const response = await graphql({ + schema, + source: ` + mutation { + unignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on DomainError { + code + description + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const error = [new GraphQLError('Unable to stop ignoring CVE. Please try again.')] + expect(response.errors).toEqual(error) + + const expectConsoleOutput = [ + `Transaction commit error occurred when user: "${superAdminUser._key}" attempted to unignore CVE "${cve}" on domain "${domain._key}", error: Error: Transaction commit error`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + + it('successfully unignores a CVE', async () => { + // Ensure CSV is ignored + const currentDomainState = await (await query`RETURN DOCUMENT(domains, ${domain._key}).ignoredCves || []`).next() + expect(currentDomainState).toEqual([cve]) + + const response = await graphql({ + schema, + source: ` + mutation { + unignoreCve(input: { domainId: "${toGlobalId('domain', domain._key)}", ignoredCve: "${cve}" }) { + result { + ... on Domain { + domain + ignoredCves + } + } + } + }`, + rootValue: null, + contextValue: superAdminContext, + }) + + const expectedResponse = { + data: { + unignoreCve: { + result: { + domain: domain.domain, + ignoredCves: [], + }, + }, + }, + } + expect(response).toEqual(expectedResponse) + + const domainCursor = await query` + FOR domain IN domains + FILTER domain._key == ${domain._key} + RETURN domain + ` + const domainArr = await domainCursor.all() + const domainObj = domainArr[0] + expect(domainObj.ignoredCves).toEqual([]) + + const expectConsoleOutput = [ + `User: "${superAdminUser._key}" successfully unignored CVE "${cve}" on domain: "${domain._key}".`, + ] + expect(consoleOutput).toEqual(expectConsoleOutput) + }) + }) +}) diff --git a/api/src/domain/mutations/__tests__/update-domain.test.js b/api/src/domain/mutations/__tests__/update-domain.test.js index a5ce757ca..4108198ba 100644 --- a/api/src/domain/mutations/__tests__/update-domain.test.js +++ b/api/src/domain/mutations/__tests__/update-domain.test.js @@ -1,6 +1,7 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as rawGraphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { createQuerySchema } from '../../../query' @@ -8,7 +9,18 @@ import { createMutationSchema } from '../../../mutation' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput, slugify } from '../../../validators' -import { checkPermission, userRequired, verifiedRequired, tfaRequired, checkDomainPermission } from '../../../auth' +import { + checkPermission, + userRequired, + verifiedRequired, + tfaRequired, + checkDomainPermission, + AuthDataSource, +} from '../../../auth' +import { DomainDataSource } from '../../data-source' +import { OrganizationDataSource } from '../../../organization/data-source' +import { TagsDataSource } from '../../../tags/data-source' +import { AuditLogsDataSource } from '../../../audit-logs/data-source' import { loadDkimSelectorsByDomainId, loadDomainByKey } from '../../loaders' import { loadOrgByKey } from '../../../organization/loaders' import { loadUserByKey } from '../../../user/loaders' @@ -17,8 +29,55 @@ import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url } = process.env +const withDataSources = (contextValue) => { + const query = contextValue?.query + const transaction = contextValue?.transaction + const collections = contextValue?.collections + const userKey = contextValue?.userKey + const i18n = contextValue?.i18n + const language = contextValue?.request?.language + const cleanseInput = contextValue?.validators?.cleanseInput + + const domainDataSource = + contextValue?.dataSources?.domain || new DomainDataSource({ query, userKey, i18n, transaction, collections }) + if (contextValue?.loaders?.loadDomainByKey) { + domainDataSource.byKey = contextValue.loaders.loadDomainByKey + } + + const organizationDataSource = + contextValue?.dataSources?.organization || + new OrganizationDataSource({ query, userKey, i18n, language, cleanseInput, transaction, collections }) + if (contextValue?.loaders?.loadOrgByKey) { + organizationDataSource.byKey = contextValue.loaders.loadOrgByKey + } + + const tagsDataSource = + contextValue?.dataSources?.tags || new TagsDataSource({ query, userKey, i18n, language, transaction, collections }) + + const auditLogs = + contextValue?.dataSources?.auditLogs || new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }) + + return { + ...contextValue, + dataSources: { + ...contextValue?.dataSources, + domain: domainDataSource, + organization: organizationDataSource, + tags: tagsDataSource, + auditLogs, + }, + } +} + +const graphql = ({ contextValue, ...args }) => { + return rawGraphql({ + ...args, + contextValue: withDataSources(contextValue), + }) +} + describe('updating a domain', () => { - let query, drop, truncate, schema, collections, transaction, user + let query, drop, truncate, schema, collections, transaction, publish, user const consoleOutput = [] const mockedInfo = (output) => consoleOutput.push(output) @@ -65,6 +124,7 @@ describe('updating a domain', () => { schema: dbschema, })) + publish = jest.fn() }) beforeEach(async () => { user = await collections.users.save({ @@ -99,6 +159,7 @@ describe('updating a domain', () => { domain = await collections.domains.save({ domain: 'test.gc.ca', lastRan: null, + selectors: [], }) const selector1 = await collections.selectors.save({ selector: 'selector1' }) const selector2 = await collections.selectors.save({ selector: 'selector2' }) @@ -114,6 +175,7 @@ describe('updating a domain', () => { _to: domain._id, _from: org._id, tags: [], + assetState: 'monitor-only', }) }) afterEach(async () => { @@ -140,7 +202,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', domain._key)}" orgId: "${toGlobalId('organization', org._key)}" - domain: "test.canada.ca" + assetState: APPROVED } ) { result { @@ -149,6 +211,7 @@ describe('updating a domain', () => { domain lastRan selectors + assetState } } } @@ -159,7 +222,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkDomainPermission: checkDomainPermission({ i18n, @@ -174,88 +239,8 @@ describe('updating a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByKey: loadDomainByKey({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.canada.ca', - lastRan: null, - selectors: ['selector1', 'selector2'], - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated domain: ${domain._key}.`]) - }) - }) - describe('user updates selectors', () => { - it('returns updated domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateDomain ( - input: { - domainId: "${toGlobalId('domain', domain._key)}" - orgId: "${toGlobalId('organization', org._key)}" - selectors: [ - "selector3", - "selector4" - ] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), }, validators: { cleanseInput, @@ -283,91 +268,8 @@ describe('updating a domain', () => { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', lastRan: null, - selectors: ['selector3', 'selector4'], - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated domain: ${domain._key}.`]) - }) - }) - describe('user updates domain and selectors', () => { - it('returns updated domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateDomain ( - input: { - domainId: "${toGlobalId('domain', domain._key)}" - orgId: "${toGlobalId('organization', org._key)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByKey: loadDomainByKey({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.canada.ca', - lastRan: null, - selectors: ['selector3', 'selector4'], + selectors: ['selector1', 'selector2'], + assetState: 'APPROVED', }, }, }, @@ -396,7 +298,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', domain._key)}" orgId: "${toGlobalId('organization', org._key)}" - domain: "test.canada.ca" + assetState: APPROVED } ) { result { @@ -405,6 +307,7 @@ describe('updating a domain', () => { domain lastRan selectors + assetState } } } @@ -415,7 +318,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkDomainPermission: checkDomainPermission({ i18n, @@ -430,88 +335,8 @@ describe('updating a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByKey: loadDomainByKey({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.canada.ca', - lastRan: null, - selectors: ['selector1', 'selector2'], - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated domain: ${domain._key}.`]) - }) - }) - describe('user updates selectors', () => { - it('returns updated domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateDomain ( - input: { - domainId: "${toGlobalId('domain', domain._key)}" - orgId: "${toGlobalId('organization', org._key)}" - selectors: [ - "selector3", - "selector4" - ] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), }, validators: { cleanseInput, @@ -539,91 +364,8 @@ describe('updating a domain', () => { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', lastRan: null, - selectors: ['selector3', 'selector4'], - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated domain: ${domain._key}.`]) - }) - }) - describe('user updates domain and selectors', () => { - it('returns updated domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateDomain ( - input: { - domainId: "${toGlobalId('domain', domain._key)}" - orgId: "${toGlobalId('organization', org._key)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByKey: loadDomainByKey({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.canada.ca', - lastRan: null, - selectors: ['selector3', 'selector4'], + selectors: ['selector1', 'selector2'], + assetState: 'APPROVED', }, }, }, @@ -652,7 +394,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', domain._key)}" orgId: "${toGlobalId('organization', org._key)}" - domain: "test.canada.ca" + assetState: APPROVED } ) { result { @@ -661,6 +403,7 @@ describe('updating a domain', () => { domain lastRan selectors + assetState } } } @@ -671,7 +414,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkDomainPermission: checkDomainPermission({ i18n, @@ -686,88 +431,8 @@ describe('updating a domain', () => { verifiedRequired: verifiedRequired({}), tfaRequired: tfaRequired({}), }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByKey: loadDomainByKey({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.canada.ca', - lastRan: null, - selectors: ['selector1', 'selector2'], - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated domain: ${domain._key}.`]) - }) - }) - describe('user updates selectors', () => { - it('returns updated domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateDomain ( - input: { - domainId: "${toGlobalId('domain', domain._key)}" - orgId: "${toGlobalId('organization', org._key)}" - selectors: [ - "selector3", - "selector4" - ] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), }, validators: { cleanseInput, @@ -795,91 +460,8 @@ describe('updating a domain', () => { id: toGlobalId('domain', domain._key), domain: 'test.gc.ca', lastRan: null, - selectors: ['selector3', 'selector4'], - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated domain: ${domain._key}.`]) - }) - }) - describe('user updates domain and selectors', () => { - it('returns updated domain', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateDomain ( - input: { - domainId: "${toGlobalId('domain', domain._key)}" - orgId: "${toGlobalId('organization', org._key)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] - } - ) { - result { - ... on Domain { - id - domain - lastRan - selectors - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkDomainPermission: checkDomainPermission({ - i18n, - userKey: user._key, - query, - }), - checkPermission: checkPermission({ userKey: user._key, query }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - tfaRequired: tfaRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), - loadDomainByKey: loadDomainByKey({ query }), - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateDomain: { - result: { - id: toGlobalId('domain', domain._key), - domain: 'test.canada.ca', - lastRan: null, - selectors: ['selector3', 'selector4'], + selectors: ['selector1', 'selector2'], + assetState: 'APPROVED', }, }, }, @@ -907,6 +489,7 @@ describe('updating a domain', () => { fr: frenchMessages.messages, }, }) + publish = jest.fn() }) describe('domain cannot be found', () => { it('returns an error message', async () => { @@ -918,11 +501,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 1)}" orgId: "${toGlobalId('organization', 1)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -931,6 +510,7 @@ describe('updating a domain', () => { domain lastRan selectors + assetState } ... on DomainError { code @@ -946,7 +526,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn(), @@ -1003,11 +585,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 1)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1016,6 +594,7 @@ describe('updating a domain', () => { domain lastRan selectors + assetState } ... on DomainError { code @@ -1031,7 +610,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn(), @@ -1088,11 +669,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1116,7 +693,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn(), @@ -1174,11 +753,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1202,7 +777,9 @@ describe('updating a domain', () => { query: jest.fn().mockReturnValue({ count: 0 }), collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -1260,11 +837,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1288,7 +861,9 @@ describe('updating a domain', () => { query: jest.fn().mockRejectedValue(new Error('database error')), collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -1338,11 +913,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1367,8 +938,10 @@ describe('updating a domain', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -1417,11 +990,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1452,8 +1021,10 @@ describe('updating a domain', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), }), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -1514,11 +1085,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 1)}" orgId: "${toGlobalId('organization', 1)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1542,7 +1109,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn(), @@ -1599,11 +1168,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 1)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1627,7 +1192,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn(), @@ -1684,11 +1251,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1712,7 +1275,9 @@ describe('updating a domain', () => { query, collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue(undefined), userRequired: jest.fn(), @@ -1770,11 +1335,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1798,7 +1359,9 @@ describe('updating a domain', () => { query: jest.fn().mockReturnValue({ count: 0 }), collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -1856,11 +1419,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1884,7 +1443,9 @@ describe('updating a domain', () => { query: jest.fn().mockRejectedValue(new Error('database error')), collections: collectionNames, transaction, + publish, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -1934,11 +1495,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -1963,8 +1520,10 @@ describe('updating a domain', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), + abort: jest.fn(), }), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), @@ -2013,11 +1572,7 @@ describe('updating a domain', () => { input: { domainId: "${toGlobalId('domain', 123)}" orgId: "${toGlobalId('organization', 123)}" - domain: "test.canada.ca" - selectors: [ - "selector3", - "selector4" - ] + assetState: APPROVED } ) { result { @@ -2048,8 +1603,10 @@ describe('updating a domain', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), }), userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), diff --git a/api/src/domain/mutations/__tests__/update-domains-by-domain-ids.test.js b/api/src/domain/mutations/__tests__/update-domains-by-domain-ids.test.js new file mode 100644 index 000000000..201995335 --- /dev/null +++ b/api/src/domain/mutations/__tests__/update-domains-by-domain-ids.test.js @@ -0,0 +1,287 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as rawGraphql, GraphQLSchema } from 'graphql' +import { toGlobalId } from 'graphql-relay' +import { setupI18n } from '@lingui/core' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' + +import { createQuerySchema } from '../../../query' +import { createMutationSchema } from '../../../mutation' +import { cleanseInput } from '../../../validators' +import { checkPermission, userRequired, saltedHash, verifiedRequired, tfaRequired } from '../../../auth' +import { DomainDataSource } from '../../data-source' +import { OrganizationDataSource } from '../../../organization/data-source' +import { TagsDataSource } from '../../../tags/data-source' +import { AuditLogsDataSource } from '../../../audit-logs/data-source' +import { loadTagByTagId } from '../../../tags/loaders' +import { loadOrgByKey } from '../../../organization/loaders' +import { loadUserByKey } from '../../../user/loaders' +import dbschema from '../../../../database.json' +import { collectionNames } from '../../../collection-names' + +const { DB_PASS: rootPass, DB_URL: url, HASHING_SECRET } = process.env + +const withDataSources = (contextValue) => { + const query = contextValue?.query + const transaction = contextValue?.transaction + const collections = contextValue?.collections + const userKey = contextValue?.userKey + const i18n = contextValue?.i18n + const language = contextValue?.request?.language + const cleanseInput = contextValue?.validators?.cleanseInput + + const domainDataSource = + contextValue?.dataSources?.domain || new DomainDataSource({ query, userKey, i18n, transaction, collections }) + + const organizationDataSource = + contextValue?.dataSources?.organization || + new OrganizationDataSource({ query, userKey, i18n, language, cleanseInput, transaction, collections }) + if (contextValue?.loaders?.loadOrgByKey) { + organizationDataSource.byKey = contextValue.loaders.loadOrgByKey + } + + const tagsDataSource = + contextValue?.dataSources?.tags || new TagsDataSource({ query, userKey, i18n, language, transaction, collections }) + if (contextValue?.loaders?.loadTagByTagId) { + tagsDataSource.byTagId = contextValue.loaders.loadTagByTagId + } + + const auditLogs = + contextValue?.dataSources?.auditLogs || new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }) + + return { + ...contextValue, + dataSources: { + ...contextValue?.dataSources, + domain: domainDataSource, + organization: organizationDataSource, + tags: tagsDataSource, + auditLogs, + }, + } +} + +const graphql = ({ contextValue, ...args }) => { + return rawGraphql({ + ...args, + contextValue: withDataSources(contextValue), + }) +} + +describe('updateDomainsByDomainIds mutation', () => { + let query, drop, i18n, truncate, schema, collections, transaction, user, org, tag + + const consoleOutput = [] + const mockedWarn = (output) => consoleOutput.push(output) + const mockedError = (output) => consoleOutput.push(output) + beforeAll(async () => { + console.warn = mockedWarn + console.error = mockedError + schema = new GraphQLSchema({ + query: createQuerySchema(), + mutation: createMutationSchema(), + }) + }) + afterEach(() => { + consoleOutput.length = 0 + }) + beforeAll(async () => { + ;({ query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + }) + beforeEach(async () => { + user = await collections.users.save({ + userName: 'test.account@istio.actually.exists', + emailValidated: true, + tfaSendMethod: 'email', + }) + org = await collections.organizations.save({ + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + tag = await collections.tags.save({ + tagId: 'tag-1', + tagName: 'Test Tag', + visible: true, + ownership: 'global', + organizations: [org._key], + }) + }) + afterEach(async () => { + await truncate() + }) + afterAll(async () => { + await drop() + }) + + describe('user has super admin permission', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + }) + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'super_admin', + }) + }) + it('updates domains by domainIds', async () => { + // Insert a domain and claim for the org + const domain = await collections.domains.save({ domain: 'test.domain.gov' }) + await collections.claims.save({ + _from: org._id, + _to: domain._id, + tags: [], + }) + const response = await graphql({ + schema, + source: ` + mutation { + updateDomainsByDomainIds( + input: { + orgId: "${toGlobalId('organizations', org._key)}" + tags: ["${tag.tagId}"] + domainIds: ["${toGlobalId('domains', domain._key)}"] + } + ) { + result { + ... on DomainBulkResult { + status + } + ... on DomainError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: { + request: { language: 'en' }, + i18n, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ userKey: user._key, query }), + saltedHash: saltedHash(HASHING_SECRET), + userRequired: userRequired({ userKey: user._key, loadUserByKey: loadUserByKey({ query }) }), + verifiedRequired: verifiedRequired({}), + tfaRequired: tfaRequired({}), + }, + loaders: { + loadTagByTagId: loadTagByTagId({ query }), + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + }, + validators: { cleanseInput }, + }, + }) + expect(response.data.updateDomainsByDomainIds.result.status).toMatch( + /Successfully updated 1 domain\(s\) in treasury-board-secretariat/, + ) + }) + }) + + describe('user does not have permission', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + }) + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'user', + }) + }) + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateDomainsByDomainIds( + input: { + orgId: "${toGlobalId('organizations', org._key)}" + tags: ["${tag.tagId}"] + domainIds: [] + } + ) { + result { + ... on DomainBulkResult { + status + } + ... on DomainError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: { + request: { language: 'en' }, + i18n, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ userKey: user._key, query }), + saltedHash: saltedHash(HASHING_SECRET), + userRequired: userRequired({ userKey: user._key, loadUserByKey: loadUserByKey({ query }) }), + verifiedRequired: verifiedRequired({}), + tfaRequired: tfaRequired({}), + }, + loaders: { + loadTagByTagId: loadTagByTagId({ query }), + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + }, + validators: { cleanseInput }, + }, + }) + expect(response.data.updateDomainsByDomainIds.result.code).toBe(400) + expect(response.data.updateDomainsByDomainIds.result.description).toMatch(/Permission Denied/) + }) + }) +}) diff --git a/api/src/domain/mutations/__tests__/update-domains-by-filters.test.js b/api/src/domain/mutations/__tests__/update-domains-by-filters.test.js new file mode 100644 index 000000000..37f2bfb9d --- /dev/null +++ b/api/src/domain/mutations/__tests__/update-domains-by-filters.test.js @@ -0,0 +1,289 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { graphql as rawGraphql, GraphQLSchema } from 'graphql' +import { toGlobalId } from 'graphql-relay' +import { setupI18n } from '@lingui/core' +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' + +import { createQuerySchema } from '../../../query' +import { createMutationSchema } from '../../../mutation' +import { cleanseInput } from '../../../validators' +import { checkPermission, userRequired, saltedHash, verifiedRequired, tfaRequired } from '../../../auth' +import { DomainDataSource } from '../../data-source' +import { OrganizationDataSource } from '../../../organization/data-source' +import { TagsDataSource } from '../../../tags/data-source' +import { AuditLogsDataSource } from '../../../audit-logs/data-source' +import { loadTagByTagId } from '../../../tags/loaders' +import { loadOrgByKey } from '../../../organization/loaders' +import { loadUserByKey } from '../../../user/loaders' +import dbschema from '../../../../database.json' +import { collectionNames } from '../../../collection-names' + +const { DB_PASS: rootPass, DB_URL: url, HASHING_SECRET } = process.env + +const withDataSources = (contextValue) => { + const query = contextValue?.query + const transaction = contextValue?.transaction + const collections = contextValue?.collections + const userKey = contextValue?.userKey + const i18n = contextValue?.i18n + const language = contextValue?.request?.language + const cleanseInput = contextValue?.validators?.cleanseInput + + const domainDataSource = + contextValue?.dataSources?.domain || new DomainDataSource({ query, userKey, i18n, transaction, collections }) + + const organizationDataSource = + contextValue?.dataSources?.organization || + new OrganizationDataSource({ query, userKey, i18n, language, cleanseInput, transaction, collections }) + if (contextValue?.loaders?.loadOrgByKey) { + organizationDataSource.byKey = contextValue.loaders.loadOrgByKey + } + + const tagsDataSource = + contextValue?.dataSources?.tags || new TagsDataSource({ query, userKey, i18n, language, transaction, collections }) + if (contextValue?.loaders?.loadTagByTagId) { + tagsDataSource.byTagId = contextValue.loaders.loadTagByTagId + } + + const auditLogs = + contextValue?.dataSources?.auditLogs || new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }) + + return { + ...contextValue, + dataSources: { + ...contextValue?.dataSources, + domain: domainDataSource, + organization: organizationDataSource, + tags: tagsDataSource, + auditLogs, + }, + } +} + +const graphql = ({ contextValue, ...args }) => { + return rawGraphql({ + ...args, + contextValue: withDataSources(contextValue), + }) +} + +describe('updateDomainsByFilters mutation', () => { + let query, drop, i18n, truncate, schema, collections, transaction, user, org, tag + + const consoleOutput = [] + const mockedWarn = (output) => consoleOutput.push(output) + const mockedError = (output) => consoleOutput.push(output) + beforeAll(async () => { + console.warn = mockedWarn + console.error = mockedError + schema = new GraphQLSchema({ + query: createQuerySchema(), + mutation: createMutationSchema(), + }) + }) + afterEach(() => { + consoleOutput.length = 0 + }) + beforeAll(async () => { + ;({ query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + }) + beforeEach(async () => { + user = await collections.users.save({ + userName: 'test.account@istio.actually.exists', + emailValidated: true, + tfaSendMethod: 'email', + }) + org = await collections.organizations.save({ + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, + }) + tag = await collections.tags.save({ + tagId: 'tag-1', + tagName: 'Test Tag', + visible: true, + ownership: 'global', + organizations: [org._key], + }) + }) + afterEach(async () => { + await truncate() + }) + afterAll(async () => { + await drop() + }) + + describe('user has super admin permission', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + }) + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'super_admin', + }) + }) + it('updates domains by filters', async () => { + // Insert a domain and claim for the org + const domain = await collections.domains.save({ domain: 'test.domain.gov' }) + await collections.claims.save({ + _from: org._id, + _to: domain._id, + tags: [], + }) + const response = await graphql({ + schema, + source: ` + mutation { + updateDomainsByFilters( + input: { + orgId: "${toGlobalId('organizations', org._key)}" + tags: ["${tag.tagId}"] + filters: [] + search: "test.domain.gov" + } + ) { + result { + ... on DomainBulkResult { + status + } + ... on DomainError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: { + request: { language: 'en' }, + i18n, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ userKey: user._key, query }), + saltedHash: saltedHash(HASHING_SECRET), + userRequired: userRequired({ userKey: user._key, loadUserByKey: loadUserByKey({ query }) }), + verifiedRequired: verifiedRequired({}), + tfaRequired: tfaRequired({}), + }, + loaders: { + loadTagByTagId: loadTagByTagId({ query }), + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + }, + validators: { cleanseInput }, + }, + }) + expect(response.data.updateDomainsByFilters.result.status).toMatch( + /Successfully updated 1 domain\(s\) in treasury-board-secretariat/, + ) + }) + }) + + describe('user does not have permission', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + }) + beforeEach(async () => { + await collections.affiliations.save({ + _from: org._id, + _to: user._id, + permission: 'user', + }) + }) + it('returns an error message', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateDomainsByFilters( + input: { + orgId: "${toGlobalId('organizations', org._key)}" + tags: ["${tag.tagId}"] + filters: [] + search: "test.domain.gov" + } + ) { + result { + ... on DomainBulkResult { + status + } + ... on DomainError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: { + request: { language: 'en' }, + i18n, + query, + collections: collectionNames, + transaction, + userKey: user._key, + auth: { + checkPermission: checkPermission({ userKey: user._key, query }), + saltedHash: saltedHash(HASHING_SECRET), + userRequired: userRequired({ userKey: user._key, loadUserByKey: loadUserByKey({ query }) }), + verifiedRequired: verifiedRequired({}), + tfaRequired: tfaRequired({}), + }, + loaders: { + loadTagByTagId: loadTagByTagId({ query }), + loadOrgByKey: loadOrgByKey({ query, language: 'en' }), + }, + validators: { cleanseInput }, + }, + }) + expect(response.data.updateDomainsByFilters.result.code).toBe(400) + expect(response.data.updateDomainsByFilters.result.description).toMatch(/Permission Denied/) + }) + }) +}) diff --git a/api/src/domain/mutations/add-organizations-domains.js b/api/src/domain/mutations/add-organizations-domains.js index 58c330485..d2b71e764 100644 --- a/api/src/domain/mutations/add-organizations-domains.js +++ b/api/src/domain/mutations/add-organizations-domains.js @@ -4,7 +4,7 @@ import { t } from '@lingui/macro' import { bulkModifyDomainsUnion } from '../unions' import { Domain } from '../../scalars' -import { logActivity } from '../../audit-logs/mutations/log-activity' +import { AssetStateEnums } from '../../enums' export const addOrganizationsDomains = new mutationWithClientMutationId({ name: 'AddOrganizationsDomains', @@ -18,10 +18,6 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ type: new GraphQLNonNull(new GraphQLList(Domain)), description: 'Url that you would like to be added to the database.', }, - hideNewDomains: { - type: GraphQLBoolean, - description: 'New domains will be hidden.', - }, tagNewDomains: { type: GraphQLBoolean, description: 'New domains will be tagged with NEW.', @@ -30,6 +26,10 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ type: GraphQLBoolean, description: 'New domains will be tagged with STAGING.', }, + assetState: { + type: AssetStateEnums, + description: "State each new domain will be given to define its relation to the org's attack surface", + }, audit: { type: GraphQLBoolean, description: 'Audit logs will be created.', @@ -51,8 +51,9 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ collections, transaction, userKey, + request: { ip }, auth: { checkPermission, saltedHash, userRequired, verifiedRequired, tfaRequired }, - loaders: { loadDomainByDomain, loadOrgByKey }, + dataSources: { domain: domainDS, organization: orgDS, auditLogs }, validators: { cleanseInput }, }, ) => { @@ -72,13 +73,6 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ domains = [] } - let hideNewDomains - if (typeof args.hideNewDomains !== 'undefined') { - hideNewDomains = args.hideNewDomains - } else { - hideNewDomains = false - } - let tagNewDomains if (typeof args.tagNewDomains !== 'undefined') { tagNewDomains = args.tagNewDomains @@ -93,6 +87,8 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ tagStagingDomains = false } + const assetState = cleanseInput(args.assetState) || 'approved' + let audit if (typeof args.audit !== 'undefined') { audit = args.audit @@ -101,7 +97,7 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ } // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn(`User: ${userKey} attempted to add domains to an organization: ${orgId} that does not exist.`) @@ -128,10 +124,10 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ const tags = [] if (tagNewDomains) { - tags.push({ en: 'NEW', fr: 'NOUVEAU' }) + tags.push('new-nouveau') } if (tagStagingDomains) { - tags.push({ en: 'STAGING', fr: 'DÉV' }) + tags.push('staging-dév') } const updatedProperties = [] @@ -142,13 +138,6 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ newValue: tags, }) } - if (hideNewDomains) { - updatedProperties.push({ - name: 'hidden', - oldValue: null, - newValue: hideNewDomains, - }) - } let domainCount = 0 @@ -202,7 +191,7 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ } // Check to see if domain already exists in db - const checkDomain = await loadDomainByDomain.load(insertDomain.domain) + const checkDomain = await domainDS.byDomain.load(insertDomain.domain) // Setup Transaction const trx = await transaction(collections) @@ -226,6 +215,7 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain: ${err}`) + await trx.abort() continue } @@ -236,6 +226,7 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Cursor error occurred for user: ${userKey} after inserting new domain and gathering its domain info: ${err}`, ) + await trx.abort() continue } @@ -248,12 +239,13 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ _from: ${org._id}, _to: ${insertedDomain._id}, tags: ${tags}, - hidden: ${hideNewDomains} + assetState: ${assetState}, } INTO claims `, ) } catch (err) { console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain edge: ${err}`) + await trx.abort() continue } } else { @@ -266,12 +258,12 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ _from: ${org._id}, _to: ${checkDomain._id}, tags: ${tags}, - hidden: ${hideNewDomains} } INTO claims `, ) } catch (err) { console.error(`Transaction step error occurred for user: ${userKey} when inserting domain edge: ${err}`) + await trx.abort() continue } } @@ -280,19 +272,18 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Transaction commit error occurred while user: ${userKey} was creating domains: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to create domains. Please try again.`)) } if (audit) { console.info(`User: ${userKey} successfully added domain: ${insertDomain.domain} to org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'add', target: { @@ -311,14 +302,12 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ if (!audit) { console.info(`User: ${userKey} successfully added ${domainCount} domain(s) to org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'add', target: { diff --git a/api/src/domain/mutations/create-domain.js b/api/src/domain/mutations/create-domain.js index fc8352bdd..316be8dcd 100644 --- a/api/src/domain/mutations/create-domain.js +++ b/api/src/domain/mutations/create-domain.js @@ -1,12 +1,13 @@ -import { GraphQLNonNull, GraphQLList, GraphQLID, GraphQLBoolean } from 'graphql' +import { GraphQLNonNull, GraphQLList, GraphQLID, GraphQLBoolean, GraphQLString } from 'graphql' import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { createDomainUnion } from '../unions' -import { Domain, SelectorsInput } from '../../scalars' -import { logActivity } from '../../audit-logs/mutations/log-activity' -import { inputTag } from '../inputs/domain-tag' -import { OutsideDomainCommentEnum } from '../../enums' +import { Domain } from '../../scalars' +import { AssetStateEnums } from '../../enums' +import { headers } from 'nats' +import { CvdEnrollmentInputOptions } from '../../additional-findings/input/cvd-enrollment-options' +import ac from '../../access-control' export const createDomain = new mutationWithClientMutationId({ name: 'CreateDomain', @@ -20,25 +21,26 @@ export const createDomain = new mutationWithClientMutationId({ type: new GraphQLNonNull(Domain), description: 'Url that you would like to be added to the database.', }, - selectors: { - type: new GraphQLList(SelectorsInput), - description: 'DKIM selector strings corresponding to this domain.', - }, tags: { description: 'List of labelled tags users have applied to the domain.', - type: new GraphQLList(inputTag), - }, - hidden: { - description: "Value that determines if the domain is excluded from an organization's score.", - type: GraphQLBoolean, + type: new GraphQLList(GraphQLString), }, archived: { description: 'Value that determines if the domain is excluded from the scanning process.', type: GraphQLBoolean, }, - outsideComment: { - description: 'Comment describing reason for adding out-of-scope domain.', - type: OutsideDomainCommentEnum, + assetState: { + description: 'Value that determines how the domain relates to the organization.', + type: new GraphQLNonNull(AssetStateEnums), + }, + cvdEnrollment: { + description: + 'The Coordinated Vulnerability Disclosure (CVD) enrollment details for this domain, including HackerOne integration status and CVSS requirements.', + type: CvdEnrollmentInputOptions, + }, + highAvailability: { + description: 'Value that determines if the service is scanned for uptime.', + type: GraphQLBoolean, }, }), outputFields: () => ({ @@ -53,14 +55,10 @@ export const createDomain = new mutationWithClientMutationId({ { i18n, request, - query, - language, - collections, - transaction, userKey, publish, auth: { checkPermission, saltedHash, userRequired, tfaRequired, verifiedRequired }, - loaders: { loadDomainByDomain, loadOrgByKey }, + dataSources: { domain: domainDS, tags: tagsDS, organization: orgDS, auditLogs }, validators: { cleanseInput }, }, ) => { @@ -74,16 +72,19 @@ export const createDomain = new mutationWithClientMutationId({ const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) const domain = cleanseInput(args.domain) - let selectors - if (typeof args.selectors !== 'undefined') { - selectors = args.selectors.map((selector) => cleanseInput(selector)) - } else { - selectors = [] - } - let tags if (typeof args.tags !== 'undefined') { - tags = args.tags + tags = await tagsDS.byTagId.loadMany( + args.tags.map((tag) => { + return cleanseInput(tag) + }), + ) + tags = tags + .filter(({ visible, ownership, organizations }) => { + // Filter out tags that are not visible or do not belong to the org + return visible && (ownership === 'global' || organizations.some((org) => org._id === orgId)) + }) + .map((tag) => tag.tagId) } else { tags = [] } @@ -95,33 +96,18 @@ export const createDomain = new mutationWithClientMutationId({ archived = false } - let hidden - if (typeof args.hidden !== 'undefined') { - hidden = args.hidden + let assetState + if (typeof args.assetState !== 'undefined') { + assetState = cleanseInput(args.assetState) } else { - hidden = false + assetState = 'approved' } - let outsideComment - if (typeof args.outsideComment !== 'undefined') { - outsideComment = cleanseInput(args.outsideComment) - } else { - outsideComment = '' - } - - if (tags?.find(({ en }) => en === 'OUTSIDE')) { - if (outsideComment === '') { - console.warn(`User: ${userKey} attempted to create a domain with the OUTSIDE tag without providing a comment.`) - return { - _type: 'error', - code: 400, - description: i18n._(t`Please provide a comment when adding an outside domain.`), - } - } - } + const cvdEnrollment = args.cvdEnrollment || { status: 'not-enrolled' } + const highAvailability = args.highAvailability || false // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn(`User: ${userKey} attempted to create a domain to an organization: ${orgId} that does not exist.`) @@ -135,59 +121,67 @@ export const createDomain = new mutationWithClientMutationId({ // Check to see if user belongs to org const permission = await checkPermission({ orgId: org._id }) - if (!['admin', 'owner', 'super_admin'].includes(permission)) { + if (!ac.can(permission).createOwn('domain').granted) { console.warn( `User: ${userKey} attempted to create a domain in: ${org.slug}, however they do not have permission to do so.`, ) return { _type: 'error', - code: 400, + code: 403, description: i18n._(t`Permission Denied: Please contact organization user for help with creating domain.`), } } + // ensure only owners can enroll or deny domains + if ( + !ac.can(permission).createOwn('cvd-enrollment').granted && + ['enrolled', 'deny'].includes(cvdEnrollment.status) + ) { + console.warn( + `User: ${userKey} attempted to update the CVD enrollment for domain: ${domain} in org: ${orgId}, however they do not have permission in that org.`, + ) + cvdEnrollment.status = cvdEnrollment.status === 'enrolled' ? 'pending' : 'not-enrolled' + } + + if (!ac.can(permission).createAny('domain').granted && highAvailability === true) { + console.warn( + `User: ${userKey} attempted to create a high availability domain in: ${org.slug}, however they do not have permission to do so.`, + ) + return { + _type: 'error', + code: 403, + description: i18n._(t`Permission Denied: Please contact super admin for help with creating domain.`), + } + } + const insertDomain = { domain: domain.toLowerCase(), lastRan: null, hash: saltedHash(domain.toLowerCase()), status: { - certificates: null, - dkim: null, - dmarc: null, - https: null, - spf: null, - ssl: null, + certificates: 'info', + ciphers: 'info', + curves: 'info', + dkim: 'info', + dmarc: 'info', + hsts: 'info', + https: 'info', + protocols: 'info', + spf: 'info', + ssl: 'info', }, - archived: archived, - } - - // Check to see if domain already belongs to same org - let checkDomainCursor - try { - checkDomainCursor = await query` - WITH claims, domains, organizations - LET domainIds = (FOR domain IN domains FILTER domain.domain == ${insertDomain.domain} RETURN { id: domain._id }) - FOR domainId IN domainIds - LET domainEdges = (FOR v, e IN 1..1 ANY domainId.id claims RETURN { _from: e._from }) - FOR domainEdge IN domainEdges - LET org = DOCUMENT(domainEdge._from) - FILTER org._key == ${org._key} - RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev }, TRANSLATE(${request.language}, org.orgDetails)) - ` - } catch (err) { - console.error(`Database error occurred while running check to see if domain already exists in an org: ${err}`) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) + archived, + ignoreRua: false, + cvdEnrollment, + highAvailability, } - let checkOrgDomain - try { - checkOrgDomain = await checkDomainCursor.next() - } catch (err) { - console.error(`Cursor error occurred while running check to see if domain already exists in an org: ${err}`) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } + const orgAlreadyClaimsDomain = await domainDS.organizationAlreadyClaimsDomainName({ + orgId: org._id, + domainName: insertDomain.domain, + }) - if (typeof checkOrgDomain !== 'undefined') { + if (orgAlreadyClaimsDomain) { console.warn( `User: ${userKey} attempted to create a domain for: ${org.slug}, however that org already has that domain claimed.`, ) @@ -198,150 +192,41 @@ export const createDomain = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - let domainCursor - try { - domainCursor = await trx.step( - () => - query` - UPSERT { domain: ${insertDomain.domain} } - INSERT ${insertDomain} - UPDATE { } - IN domains - RETURN NEW - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain: ${err}`) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - let insertedDomain - try { - insertedDomain = await domainCursor.next() - } catch (err) { - console.error(`Cursor error occurred for user: ${userKey} when inserting new domain: ${err}`) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - try { - await trx.step( - () => - query` - WITH claims - INSERT { - _from: ${org._id}, - _to: ${insertedDomain._id}, - tags: ${tags}, - hidden: ${hidden}, - outsideComment: ${outsideComment}, - firstSeen: ${new Date().toISOString()}, - } INTO claims - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain edge: ${err}`) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - for (const selector of selectors) { - // Ensure selector exists in database - let selectorDocCursor - try { - selectorDocCursor = await trx.step( - () => - query` - UPSERT { selector: ${selector} } - INSERT { selector: ${selector} } - UPDATE { } - IN selectors - RETURN NEW - `, - ) - } catch (err) { - console.error( - `Database error occurred while user: ${userKey} was creating domain: ${insertDomain.domain} while ensuring selector exists: ${err}`, - ) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - let selectorDoc - try { - selectorDoc = await selectorDocCursor.next() - } catch (err) { - console.error( - `Cursor error occurred while user: ${userKey} was creating domain: ${insertDomain.domain} while ensuring selector exists: ${err}`, - ) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - // Add selector to domain - try { - await trx.step( - () => - query` - INSERT { - _from: ${insertedDomain._id}, - _to: ${selectorDoc._id}, - } INTO domainsToSelectors - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred for user: ${userKey} when inserting new selector edge for domain: ${err}`, - ) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was creating domain: ${err}`) - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - // Clear dataloader incase anything was updated or inserted into domain - await loadDomainByDomain.clear(insertDomain.domain) - const returnDomain = await loadDomainByDomain.load(insertDomain.domain) + const returnDomain = await domainDS.create({ insertDomain, org, tags, assetState }) console.info(`User: ${userKey} successfully created ${returnDomain.domain} in org: ${org.slug}.`) const updatedProperties = [] - if (typeof insertDomain.selectors !== 'undefined' && insertDomain.selectors.length > 0) { + if (typeof tags !== 'undefined' && tags.length > 0) { updatedProperties.push({ - name: 'selectors', + name: 'tags', oldValue: [], - newValue: selectors, + newValue: tags, }) } - if (typeof tags !== 'undefined' && tags.length > 0) { + if (typeof assetState !== 'undefined') { updatedProperties.push({ - name: 'tags', - oldValue: [], - newValue: tags, + name: 'assetState', + oldValue: null, + newValue: assetState, }) } - if (typeof hidden !== 'undefined') { + if (typeof cvdEnrollment !== 'undefined') { updatedProperties.push({ - name: 'hidden', + name: 'cvdEnrollment', oldValue: null, - newValue: hidden, + newValue: cvdEnrollment.enrollment, }) } - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: request.ip, }, action: 'add', target: { @@ -350,29 +235,50 @@ export const createDomain = new mutationWithClientMutationId({ organization: { id: org._key, name: org.name, - }, // name of resource being acted upon - resourceType: 'domain', // user, org, domain + }, + resourceType: 'domain', }, - reason: outsideComment !== '' ? outsideComment : null, }) - await publish({ - channel: `domains.${returnDomain._key}`, - msg: { - domain: returnDomain.domain, - domain_key: returnDomain._key, - hash: returnDomain.hash, - user_key: null, // only used for One Time Scans - shared_id: null, // only used for One Time Scans - }, - }) + const hdrs = headers() + hdrs.set('priority', 'high') + + try { + await publish({ + channel: 'scans.requests_priority', + msg: { + domain: returnDomain.domain, + domain_key: returnDomain._key, + hash: returnDomain.hash, + user_key: null, // only used for One Time Scans + shared_id: null, // only used for One Time Scans + }, + options: { + headers: hdrs, + }, + }) + } catch (err) { + console.error(`Error publishing to NATS for domain ${returnDomain._key}: ${err}`) + } + + try { + await publish({ + channel: 'scans.add_domain_to_easm', + msg: { + domain: returnDomain.domain, + domain_key: returnDomain._key, + hash: returnDomain.hash, + user_key: null, // only used for One Time Scans + shared_id: null, // only used for One Time Scans + }, + }) + } catch (err) { + console.error(`Error publishing to NATS for domain ${returnDomain._key}: ${err}`) + } return { ...returnDomain, - claimTags: tags.map((tag) => { - return tag[language] - }), - hidden, + claimTags: tags, } }, }) diff --git a/api/src/domain/mutations/favourite-domain.js b/api/src/domain/mutations/favourite-domain.js index 01b768c9d..70eb422cf 100644 --- a/api/src/domain/mutations/favourite-domain.js +++ b/api/src/domain/mutations/favourite-domain.js @@ -24,12 +24,9 @@ export const favouriteDomain = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, auth: { userRequired, verifiedRequired }, - loaders: { loadDomainByKey }, + dataSources: { domain: domainDataSource }, validators: { cleanseInput }, }, ) => { @@ -41,7 +38,7 @@ export const favouriteDomain = new mutationWithClientMutationId({ const { type: _domainType, id: domainId } = fromGlobalId(cleanseInput(args.domainId)) // Get domain from db - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) // Check to see if domain exists if (typeof domain === 'undefined') { console.warn(`User: ${userKey} attempted to favourite ${domainId} however no domain is associated with that id.`) @@ -52,29 +49,12 @@ export const favouriteDomain = new mutationWithClientMutationId({ } } - // Check to see if domain already favourited by user - let checkDomainCursor - try { - checkDomainCursor = await query` - WITH domains - FOR v, e IN 1..1 ANY ${domain._id} favourites - FILTER e._from == ${user._id} - RETURN e - ` - } catch (err) { - console.error(`Database error occurred while running check to see if domain already favourited: ${err}`) - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } - - let checkUserDomain - try { - checkUserDomain = await checkDomainCursor.next() - } catch (err) { - console.error(`Cursor error occurred while running check to see if domain already favourited: ${err}`) - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } + const alreadyFavourited = await domainDataSource.isFavouritedByUser({ + domainId: domain._id, + userId: user._id, + }) - if (typeof checkUserDomain !== 'undefined') { + if (alreadyFavourited) { console.warn(`User: ${userKey} attempted to favourite a domain, however user already has that domain favourited.`) return { _type: 'error', @@ -83,31 +63,7 @@ export const favouriteDomain = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - try { - await trx.step( - () => - query` - WITH favourites - INSERT { - _from: ${user._id}, - _to: ${domain._id}, - } INTO favourites - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain edge: ${err}`) - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was creating domain: ${err}`) - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } + await domainDataSource.favourite({ domain, user }) console.info(`User: ${userKey} successfully favourited domain ${domain.domain}.`) diff --git a/api/src/domain/mutations/ignore-cve.js b/api/src/domain/mutations/ignore-cve.js new file mode 100644 index 000000000..1098e00e9 --- /dev/null +++ b/api/src/domain/mutations/ignore-cve.js @@ -0,0 +1,153 @@ +import { GraphQLID, GraphQLNonNull } from 'graphql' +import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' +import { t } from '@lingui/macro' + +import { CveID } from '../../scalars' +import { ignoreCveUnion } from '../unions/ignore-cve-union' + +export const ignoreCve = new mutationWithClientMutationId({ + name: 'IgnoreCve', + description: 'Ignore a CVE for a domain.', + inputFields: () => ({ + domainId: { + type: new GraphQLNonNull(GraphQLID), + description: 'The global id of the domain which is ignoring the CVE.', + }, + ignoredCve: { + description: 'The CVE ID that is being ignored.', + type: CveID, + }, + }), + outputFields: () => ({ + result: { + type: ignoreCveUnion, + description: '`IgnoreCveUnion` returning either a `Domain` or an error', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { + i18n, + query, + userKey, + request: { ip }, + auth: { userRequired, checkSuperAdmin, superAdminRequired, verifiedRequired, tfaRequired }, + validators: { cleanseInput }, + dataSources: { domain: domainDataSource, auditLogs }, + }, + ) => { + // Get User + const user = await userRequired() + verifiedRequired({ user }) + tfaRequired({ user }) + + // Only super admins can ignore CVEs + const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ user, isSuperAdmin }) + + const { id: domainId } = fromGlobalId(cleanseInput(args.domainId)) + + const ignoredCve = cleanseInput(args.ignoredCve) + + // Check to see if domain exists + const domain = await domainDataSource.byKey.load(domainId) + + if (typeof domain === 'undefined') { + console.warn(`User: "${userKey}" attempted to ignore CVE "${ignoredCve}" on unknown domain: "${domainId}".`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to ignore CVE. Please try again.`), + } + } + + const oldIgnoredCves = domain.ignoredCves || [] + + if (oldIgnoredCves.includes(ignoredCve)) { + console.warn( + `User: "${userKey}" attempted to ignore CVE "${ignoredCve}" on domain: "${domainId}" however CVE is already ignored.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`CVE is already ignored for this domain.`), + } + } + + const newIgnoredCves = Array.from(new Set([...oldIgnoredCves, ignoredCve])) + + const returnDomain = await domainDataSource.ignoreCve({ domain, ignoredCve, newIgnoredCves }) + + // Get all verified claims to domain and activityLog those organizations + try { + const orgs = await query` + FOR v, e IN 1..1 INBOUND ${domain._id} claims + FILTER v.verified == true + RETURN { + _key: v._key, + name: v.orgDetails.en.orgName, + } + ` + for await (const org of orgs) { + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: 'super_admin', + ipAddress: ip, + }, + action: 'update', + target: { + resource: domain.domain, + organization: { + id: org._key, + name: org.name, + }, + resourceType: 'domain', + updatedProperties: [ + { + name: ignoredCve, + oldValue: 'unignored', + newValue: 'ignored', + }, + ], + }, + }) + } + // Log activity for super admin logging + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: 'super_admin', + ipAddress: ip, + }, + action: 'update', + target: { + resource: domain.domain, + resourceType: 'domain', + updatedProperties: [ + { + name: ignoredCve, + oldValue: 'unignored', + newValue: 'ignored', + }, + ], + }, + }) + } catch (err) { + console.error( + `Database error occurred when user: "${userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domainId}" during activity logs, error: ${err}`, + ) + } + + console.info(`User: "${userKey}" successfully ignored CVE "${ignoredCve}" on domain: "${domainId}".`) + + returnDomain.id = returnDomain._key + + return { + ...returnDomain, + } + }, +}) diff --git a/api/src/domain/mutations/index.js b/api/src/domain/mutations/index.js index a1dcba6d3..295cc20ce 100644 --- a/api/src/domain/mutations/index.js +++ b/api/src/domain/mutations/index.js @@ -1,9 +1,13 @@ export * from './add-organizations-domains' export * from './create-domain' export * from './favourite-domain' +export * from './ignore-cve' export * from './remove-domain' export * from './remove-organizations-domains' export * from './request-discovery' export * from './request-scan' export * from './unfavourite-domain' +export * from './unignore-cve' export * from './update-domain' +export * from './update-domains-by-domain-ids' +export * from './update-domains-by-filters' diff --git a/api/src/domain/mutations/remove-domain.js b/api/src/domain/mutations/remove-domain.js index 139c8b4f0..bb0122877 100644 --- a/api/src/domain/mutations/remove-domain.js +++ b/api/src/domain/mutations/remove-domain.js @@ -3,8 +3,8 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { removeDomainUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import { DomainRemovalReasonEnum } from '../../enums' +import ac from '../../access-control' export const removeDomain = new mutationWithClientMutationId({ name: 'RemoveDomain', @@ -34,13 +34,11 @@ export const removeDomain = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByKey, loadOrgByKey }, + dataSources: { domain: domainDataSource, organization: organizationDS, auditLogs }, }, ) => { // Get User @@ -54,7 +52,7 @@ export const removeDomain = new mutationWithClientMutationId({ const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) // Get domain from db - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) // Check to see if domain exists if (typeof domain === 'undefined') { @@ -67,7 +65,7 @@ export const removeDomain = new mutationWithClientMutationId({ } // Get Org from db - const org = await loadOrgByKey.load(orgId) + const org = await organizationDS.byKey.load(orgId) // Check to see if org exists if (typeof org === 'undefined') { @@ -84,7 +82,7 @@ export const removeDomain = new mutationWithClientMutationId({ // Get permission const permission = await checkPermission({ orgId: org._id }) - if (['admin', 'owner', 'super_admin'].includes(permission) === false) { + if (!ac.can(permission).deleteOwn('domain').granted) { console.warn( `User: ${userKey} attempted to remove ${domain.domain} in ${org.slug} however they do not have permission in that org.`, ) @@ -97,7 +95,7 @@ export const removeDomain = new mutationWithClientMutationId({ // Check to see if domain belongs to verified check org // if domain returns NXDOMAIN, allow removal - if (org.verified && permission !== 'super_admin' && domain.rcode !== 'NXDOMAIN') { + if (org.verified && !ac.can(permission).deleteAny('domain').granted && domain.rcode !== 'NXDOMAIN') { console.warn( `User: ${userKey} attempted to remove ${domain.domain} in ${org.slug} but does not have permission to remove a domain from a verified check org.`, ) @@ -108,23 +106,18 @@ export const removeDomain = new mutationWithClientMutationId({ } } - // Check to see if more than one organization has a claim to this domain - let countCursor + let orgsClaimingDomain + let orgsClaimingDomainCount try { - countCursor = await query` - WITH claims, domains, organizations - FOR v, e IN 1..1 ANY ${domain._id} claims - RETURN v - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey}, when counting domain claims for domain: ${domain.domain}, error: ${err}`, - ) + const claims = await domainDataSource.organizationsClaimingDomain({ + domainId: domain._id, + domainName: domain.domain, + }) + orgsClaimingDomain = claims.organizations + orgsClaimingDomainCount = claims.count + } catch { throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) } - - // check if org has claim to domain - const orgsClaimingDomain = await countCursor.all() const orgHasDomainClaim = orgsClaimingDomain.some((orgVertex) => { return orgVertex._id === org._id }) @@ -136,213 +129,35 @@ export const removeDomain = new mutationWithClientMutationId({ throw new Error(i18n._(t`Unable to remove domain. Domain is not part of organization.`)) } - // check to see if org removing domain has ownership - let dmarcCountCursor + let hasOwnership try { - dmarcCountCursor = await query` - WITH domains, organizations, ownership - FOR v IN 1..1 OUTBOUND ${org._id} ownership - FILTER v._id == ${domain._id} - RETURN true - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey}, when counting ownership claims for domain: ${domain.domain}, error: ${err}`, - ) + hasOwnership = await domainDataSource.hasOwnershipClaim({ + orgId: org._id, + domainId: domain._id, + domainName: domain.domain, + }) + } catch { throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) } - // Setup Transaction - const trx = await transaction(collections) - - if (dmarcCountCursor.count === 1) { - try { - await trx.step( - () => query` - WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries - LET dmarcSummaryEdges = ( - FOR v, e IN 1..1 OUTBOUND ${domain._id} domainsToDmarcSummaries - RETURN { edgeKey: e._key, dmarcSummaryId: e._to } - ) - LET removeDmarcSummaryEdges = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries - OPTIONS { waitForSync: true } - ) - LET removeDmarcSummary = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key - REMOVE key IN dmarcSummaries - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred when removing dmarc summary data for user: ${userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - try { - await trx.step( - () => query` - WITH ownership, organizations, domains - LET domainEdges = ( - FOR v, e IN 1..1 INBOUND ${domain._id} ownership - REMOVE e._key IN ownership - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred when removing ownership data for user: ${userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - } - - if (countCursor.count <= 1) { - // Remove scan data - - try { - // Remove web data - await trx.step(async () => { - await query` - WITH web, webScan, domains - FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb - LET removeWebScansQuery = ( - FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans - REMOVE webScanV IN webScan - REMOVE webToWebScansV IN webToWebScans - OPTIONS { waitForSync: true } - ) - REMOVE webV IN web - REMOVE domainsWebEdge IN domainsWeb - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove web data for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - try { - // Remove DNS data - await trx.step(async () => { - await query` - WITH dns, domains - FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS - REMOVE dnsV IN dns - REMOVE domainsDNSEdge IN domainsDNS - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DNS data for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - // remove favourites - try { - await trx.step(async () => { - await query` - WITH favourites, domains - FOR fav IN favourites - FILTER fav._to == ${domain._id} - REMOVE fav IN favourites - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove favourites for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - // remove DKIM selectors - try { - await trx.step(async () => { - await query` - FOR e IN domainsToSelectors - FILTER e._from == ${domain._id} - REMOVE e IN domainsToSelectors - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DKIM selectors for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - try { - // Remove domain - await trx.step(async () => { - await query` - FOR claim IN claims - FILTER claim._to == ${domain._id} - REMOVE claim IN claims - REMOVE ${domain} IN domains - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove domain ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - } else { - try { - await trx.step(async () => { - await query` - WITH claims, domains, organizations - LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} claims RETURN { _key: e._key, _from: e._from, _to: e._to }) - LET edgeKeys = ( - FOR domainEdge IN domainEdges - FILTER domainEdge._to == ${domain._id} - FILTER domainEdge._from == ${org._id} - RETURN domainEdge._key - ) - FOR edgeKey IN edgeKeys - REMOVE edgeKey IN claims - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove claim for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - } - - // Commit transaction try { - await trx.commit() - } catch (err) { - console.error( - `Trx commit error occurred while user: ${userKey} attempted to remove ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) + await domainDataSource.remove({ + domain, + org, + orgsClaimingDomain: orgsClaimingDomainCount, + hasOwnership, + }) + } catch { throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) } console.info(`User: ${userKey} successfully removed domain: ${domain.domain} from org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + const activityPayload = { initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'remove', target: { @@ -350,11 +165,13 @@ export const removeDomain = new mutationWithClientMutationId({ organization: { id: org._key, name: org.name, - }, // name of resource being acted upon - resourceType: 'domain', // user, org, domain + }, + resourceType: 'domain', }, reason: args.reason, - }) + } + + await auditLogs.logActivity(activityPayload) return { _type: 'result', diff --git a/api/src/domain/mutations/remove-organizations-domains.js b/api/src/domain/mutations/remove-organizations-domains.js index 78c922400..730437c25 100644 --- a/api/src/domain/mutations/remove-organizations-domains.js +++ b/api/src/domain/mutations/remove-organizations-domains.js @@ -3,7 +3,6 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { bulkModifyDomainsUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import { Domain } from '../../scalars' export const removeOrganizationsDomains = new mutationWithClientMutationId({ @@ -42,9 +41,10 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ collections, transaction, userKey, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByDomain, loadOrgByKey }, + dataSources: { domain: domainDS, organization: orgDS, auditLogs }, }, ) => { // Get User @@ -77,7 +77,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ } // Get Org from db - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) // Check to see if org exists if (typeof org === 'undefined') { @@ -135,7 +135,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ const trx = await transaction(collections) // Get domain from db - const checkDomain = await loadDomainByDomain.load(domain) + const checkDomain = await domainDS.byDomain.load(domain) // Check to see if domain exists if (typeof checkDomain === 'undefined') { @@ -153,14 +153,12 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ ) if (audit) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'update', target: { @@ -184,6 +182,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Database error occurred for user: ${userKey} when attempting to archive domain: ${domain}, error: ${err}`, ) + await trx.abort() continue } } else { @@ -199,6 +198,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Database error occurred for user: ${userKey}, when counting domain claims for domain: ${checkDomain.domain}, error: ${err}`, ) + await trx.abort() continue } @@ -212,6 +212,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Error occurred for user: ${userKey}, when attempting to remove domain "${domain}" from organization with slug "${org.slug}". Organization does not have claim for domain.`, ) + await trx.abort() continue } @@ -228,6 +229,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Database error occurred for user: ${userKey}, when counting ownership claims for domain: ${checkDomain.domain}, error: ${err}`, ) + await trx.abort() continue } @@ -258,6 +260,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Trx step error occurred when removing dmarc summary data for user: ${userKey} while attempting to remove domain: ${checkDomain.domain}, error: ${err}`, ) + await trx.abort() continue } @@ -277,6 +280,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Trx step error occurred when removing ownership data for user: ${userKey} while attempting to remove domain: ${checkDomain.domain}, error: ${err}`, ) + await trx.abort() continue } } @@ -305,6 +309,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Trx step error occurred while user: ${userKey} attempted to remove web data for ${domain.domain} in org: ${org.slug}, error: ${err}`, ) + await trx.abort() continue } @@ -323,6 +328,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Trx step error occurred while user: ${userKey} attempted to remove DNS data for ${domain.domain} in org: ${org.slug}, error: ${err}`, ) + await trx.abort() continue } @@ -340,6 +346,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Trx step error occurred while user: ${userKey} attempted to remove domain ${checkDomain.domain} in org: ${org.slug}, error: ${err}`, ) + await trx.abort() continue } } else { @@ -363,6 +370,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Trx step error occurred while user: ${userKey} attempted to remove claim for ${checkDomain.domain} in org: ${org.slug}, error: ${err}`, ) + await trx.abort() continue } } @@ -374,19 +382,18 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ console.error( `Trx commit error occurred while user: ${userKey} attempted to remove domains in org: ${org.slug}, error: ${err}`, ) + await trx.abort() continue } if (audit) { console.info(`User: ${userKey} successfully removed domain: ${domain} from org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'remove', target: { @@ -407,14 +414,12 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ if (!audit) { console.info(`User: ${userKey} successfully removed ${domainCount} domain(s) from org: ${org.slug}.`) if (archiveDomains) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'update', target: { @@ -434,14 +439,12 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ }, }) } else { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'remove', target: { diff --git a/api/src/domain/mutations/request-discovery.js b/api/src/domain/mutations/request-discovery.js index 5815b52b3..ce598dff8 100644 --- a/api/src/domain/mutations/request-discovery.js +++ b/api/src/domain/mutations/request-discovery.js @@ -3,7 +3,6 @@ import { GraphQLID, GraphQLNonNull, GraphQLString } from 'graphql' import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' import { Domain } from '../../scalars' -import { logActivity } from '../../audit-logs' export const requestDiscovery = new mutationWithClientMutationId({ name: 'RequestDiscovery', @@ -28,14 +27,12 @@ export const requestDiscovery = new mutationWithClientMutationId({ mutateAndGetPayload: async ( args, { - query, - collections, - transaction, i18n, userKey, publish, + request: { ip }, auth: { checkDomainPermission, userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired }, - loaders: { loadDomainByDomain, loadOrgByKey }, + dataSources: { domain: domainDS, organization: orgDS, auditLogs }, validators: { cleanseInput }, }, ) => { @@ -63,7 +60,7 @@ export const requestDiscovery = new mutationWithClientMutationId({ } // Check to see if domain exists - const domain = await loadDomainByDomain.load(domainInput) + const domain = await domainDS.byDomain.load(domainInput) if (typeof domain === 'undefined') { console.warn( @@ -73,7 +70,7 @@ export const requestDiscovery = new mutationWithClientMutationId({ } // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn( @@ -99,21 +96,19 @@ export const requestDiscovery = new mutationWithClientMutationId({ } await publish({ - channel: `domains.${domain._key}.discovery`, + channel: `scans.discovery`, msg: { domain: domain.domain, orgId: org._id, }, }) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'discover', target: { diff --git a/api/src/domain/mutations/request-scan.js b/api/src/domain/mutations/request-scan.js index 68c5444ec..2269ef5d4 100644 --- a/api/src/domain/mutations/request-scan.js +++ b/api/src/domain/mutations/request-scan.js @@ -3,7 +3,7 @@ import { GraphQLString } from 'graphql' import { mutationWithClientMutationId } from 'graphql-relay' import { Domain } from '../../scalars' -import { logActivity } from '../../audit-logs' +import { headers } from 'nats' export const requestScan = new mutationWithClientMutationId({ name: 'RequestScan', @@ -25,13 +25,12 @@ export const requestScan = new mutationWithClientMutationId({ args, { query, - collections, - transaction, i18n, userKey, + request: { ip }, publish, auth: { checkDomainPermission, userRequired, verifiedRequired }, - loaders: { loadDomainByDomain, loadWebConnectionsByDomainId, loadWebScansByWebId }, + dataSources: { webScan, domain: domainDS, auditLogs }, validators: { cleanseInput }, }, ) => { @@ -43,7 +42,7 @@ export const requestScan = new mutationWithClientMutationId({ const domainInput = cleanseInput(args.domain) // Check to see if domain exists - const domain = await loadDomainByDomain.load(domainInput) + const domain = await domainDS.byDomain.load(domainInput) if (typeof domain === 'undefined') { console.warn( @@ -99,7 +98,7 @@ export const requestScan = new mutationWithClientMutationId({ // Check to see if a scan is already pending try { - const webConnections = await loadWebConnectionsByDomainId({ + const webConnections = await webScan.getConnectionsByDomainId({ domainId: domain._id, limit: 1, orderBy: { field: 'timestamp', direction: 'DESC' }, @@ -107,7 +106,7 @@ export const requestScan = new mutationWithClientMutationId({ }) if (webConnections.edges.length > 0) { const webConnection = webConnections.edges[0].node - const webScans = await loadWebScansByWebId({ webId: webConnection._id }) + const webScans = await webScan.getScansByWebId({ webId: webConnection._id }) webScans.forEach((result) => { const timeDifferenceInMinutes = (Date.now() - new Date(webConnection.timestamp).getTime()) / 1000 / 60 if (result.status.toUpperCase() === 'PENDING' && timeDifferenceInMinutes < 30) { @@ -125,28 +124,31 @@ export const requestScan = new mutationWithClientMutationId({ throw new Error(i18n._(t`Unable to request a one time scan. Please try again.`)) } + const hdrs = headers() + hdrs.set('priority', 'high') + await publish({ - channel: `domains.${domain._key}`, + channel: 'scans.requests_priority', msg: { domain: domain.domain, domain_key: domain._key, - selectors: domain.selectors ? domain.selectors : [], hash: domain.hash, user_key: null, // only used for One Time Scans shared_id: null, // only used for One Time Scans }, + options: { + headers: hdrs, + }, }) // Logs scan request activity for each org claiming domain for (const orgClaimingDomain of orgsClaimingDomain) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'scan', target: { diff --git a/api/src/domain/mutations/unfavourite-domain.js b/api/src/domain/mutations/unfavourite-domain.js index d1dc4f150..2cd1a9cbc 100644 --- a/api/src/domain/mutations/unfavourite-domain.js +++ b/api/src/domain/mutations/unfavourite-domain.js @@ -24,12 +24,9 @@ export const unfavouriteDomain = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, auth: { userRequired, verifiedRequired }, - loaders: { loadDomainByKey }, + dataSources: { domain: domainDataSource }, validators: { cleanseInput }, }, ) => { @@ -41,7 +38,7 @@ export const unfavouriteDomain = new mutationWithClientMutationId({ const { type: _domainType, id: domainId } = fromGlobalId(cleanseInput(args.domainId)) // Get domain from db - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) // Check to see if domain exists if (typeof domain === 'undefined') { console.warn( @@ -54,29 +51,12 @@ export const unfavouriteDomain = new mutationWithClientMutationId({ } } - // Check to see if domain already favourited by user - let checkDomainCursor - try { - checkDomainCursor = await query` - WITH domains - FOR v, e IN 1..1 ANY ${domain._id} favourites - FILTER e._from == ${user._id} - RETURN e - ` - } catch (err) { - console.error(`Database error occurred while running check to see if domain already favourited: ${err}`) - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } - - let checkUserDomain - try { - checkUserDomain = await checkDomainCursor.next() - } catch (err) { - console.error(`Cursor error occurred while running check to see if domain already favourited: ${err}`) - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } + const alreadyFavourited = await domainDataSource.isFavouritedByUser({ + domainId: domain._id, + userId: user._id, + }) - if (typeof checkUserDomain === 'undefined') { + if (!alreadyFavourited) { console.warn(`User: ${userKey} attempted to unfavourite a domain, however domain is not favourited.`) return { _type: 'error', @@ -85,37 +65,7 @@ export const unfavouriteDomain = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - try { - await trx.step( - () => - query` - WITH favourites, domains, users - LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} favourites RETURN { _key: e._key, _from: e._from, _to: e._to }) - LET edgeKeys = ( - FOR domainEdge IN domainEdges - FILTER domainEdge._to == ${domain._id} - FILTER domainEdge._from == ${user._id} - RETURN domainEdge._key - ) - FOR edgeKey IN edgeKeys - REMOVE edgeKey IN favourites - OPTIONS { waitForSync: true } - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when removing domain edge: ${err}`) - throw new Error(i18n._(t`Unable to unfavourite domain. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was unfavouriting domain: ${err}`) - throw new Error(i18n._(t`Unable to unfavourite domain. Please try again.`)) - } + await domainDataSource.unfavourite({ domain, user }) console.info(`User: ${userKey} successfully removed domain ${domain.domain} from favourites.`) diff --git a/api/src/domain/mutations/unignore-cve.js b/api/src/domain/mutations/unignore-cve.js new file mode 100644 index 000000000..80b043299 --- /dev/null +++ b/api/src/domain/mutations/unignore-cve.js @@ -0,0 +1,153 @@ +import { GraphQLID, GraphQLNonNull } from 'graphql' +import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' +import { t } from '@lingui/macro' + +import { CveID } from '../../scalars' +import { ignoreCveUnion } from '../unions/ignore-cve-union' + +export const unignoreCve = new mutationWithClientMutationId({ + name: 'UnignoreCve', + description: 'Unignore a CVE for a domain.', + inputFields: () => ({ + domainId: { + type: new GraphQLNonNull(GraphQLID), + description: 'The global id of the domain which is unignoring the CVE.', + }, + ignoredCve: { + description: 'The CVE ID that is being ignored.', + type: CveID, + }, + }), + outputFields: () => ({ + result: { + type: ignoreCveUnion, + description: '`IgnoreCveUnion` returning either a `Domain` or an error', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { + i18n, + query, + userKey, + request: { ip }, + auth: { userRequired, checkSuperAdmin, superAdminRequired, verifiedRequired, tfaRequired }, + validators: { cleanseInput }, + dataSources: { domain: domainDataSource, auditLogs }, + }, + ) => { + // Get User + const user = await userRequired() + verifiedRequired({ user }) + tfaRequired({ user }) + + // Only super admins can ignore CVEs + const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ user, isSuperAdmin }) + + const { id: domainId } = fromGlobalId(cleanseInput(args.domainId)) + + const ignoredCve = cleanseInput(args.ignoredCve) + + // Check to see if domain exists + const domain = await domainDataSource.byKey.load(domainId) + + if (typeof domain === 'undefined') { + console.warn(`User: "${userKey}" attempted to unignore CVE "${ignoredCve}" on unknown domain: "${domainId}".`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to stop ignoring CVE. Please try again.`), + } + } + + const oldIgnoredCves = domain.ignoredCves || [] + + if (!oldIgnoredCves.includes(ignoredCve)) { + console.warn( + `User: "${userKey}" attempted to unignore CVE "${ignoredCve}" on domain: "${domainId}" however CVE is not ignored.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`CVE is not ignored for this domain.`), + } + } + + const newIgnoredCves = Array.from(new Set([...oldIgnoredCves.filter((cve) => cve !== ignoredCve)])) + + const returnDomain = await domainDataSource.unignoreCve({ domain, ignoredCve, newIgnoredCves }) + + // Get all verified claims to domain and activityLog those organizations + try { + const orgs = await query` + FOR v, e IN 1..1 INBOUND ${domain._id} claims + FILTER v.verified == true + RETURN { + _key: v._key, + name: v.orgDetails.en.orgName, + } + ` + for await (const org of orgs) { + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: 'super_admin', + ipAddress: ip, + }, + action: 'update', + target: { + resource: domain.domain, + organization: { + id: org._key, + name: org.name, + }, + resourceType: 'domain', + updatedProperties: [ + { + name: ignoredCve, + oldValue: 'ignored', + newValue: 'unignored', + }, + ], + }, + }) + } + // Log activity for super admin logging + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: 'super_admin', + ipAddress: ip, + }, + action: 'update', + target: { + resource: domain.domain, + resourceType: 'domain', + updatedProperties: [ + { + name: ignoredCve, + oldValue: 'ignored', + newValue: 'unignored', + }, + ], + }, + }) + } catch (err) { + console.error( + `Database error occurred when user: "${userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domainId}" during activity logs, error: ${err}`, + ) + } + + console.info(`User: "${userKey}" successfully unignored CVE "${ignoredCve}" on domain: "${domainId}".`) + + returnDomain.id = returnDomain._key + + return { + ...returnDomain, + } + }, +}) diff --git a/api/src/domain/mutations/update-domain.js b/api/src/domain/mutations/update-domain.js index 120a71e07..4f1b423a7 100644 --- a/api/src/domain/mutations/update-domain.js +++ b/api/src/domain/mutations/update-domain.js @@ -1,12 +1,11 @@ -import { GraphQLID, GraphQLNonNull, GraphQLList, GraphQLBoolean } from 'graphql' +import { GraphQLID, GraphQLNonNull, GraphQLList, GraphQLBoolean, GraphQLString } from 'graphql' import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { updateDomainUnion } from '../unions' -import { Domain, SelectorsInput } from '../../scalars' -import { logActivity } from '../../audit-logs/mutations/log-activity' -import { inputTag } from '../inputs/domain-tag' -import { OutsideDomainCommentEnum } from '../../enums' +import { AssetStateEnums } from '../../enums' +import { CvdEnrollmentInputOptions } from '../../additional-findings/input/cvd-enrollment-options' +import ac from '../../access-control' export const updateDomain = new mutationWithClientMutationId({ name: 'UpdateDomain', @@ -20,29 +19,30 @@ export const updateDomain = new mutationWithClientMutationId({ type: new GraphQLNonNull(GraphQLID), description: 'The global ID of the organization used for permission checks.', }, - domain: { - type: Domain, - description: 'The new url of the of the old domain.', - }, - selectors: { - type: new GraphQLList(SelectorsInput), - description: 'The updated DKIM selector strings corresponding to this domain.', - }, tags: { description: 'List of labelled tags users have applied to the domain.', - type: new GraphQLList(inputTag), - }, - hidden: { - description: "Value that determines if the domain is excluded from an organization's score.", - type: GraphQLBoolean, + type: new GraphQLList(GraphQLString), }, archived: { description: 'Value that determines if the domain is excluded from the scanning process.', type: GraphQLBoolean, }, - outsideComment: { - description: 'Comment describing reason for adding out-of-scope domain.', - type: OutsideDomainCommentEnum, + ignoreRua: { + description: 'Boolean value that determines if the domain should ignore rua reports.', + type: GraphQLBoolean, + }, + assetState: { + description: 'Value that determines how the domain relates to the organization.', + type: AssetStateEnums, + }, + cvdEnrollment: { + description: + 'The Coordinated Vulnerability Disclosure (CVD) enrollment details for this domain, including HackerOne integration status and CVSS requirements.', + type: CvdEnrollmentInputOptions, + }, + highAvailability: { + description: 'Value that determines if the service is scanned for uptime.', + type: GraphQLBoolean, }, }), outputFields: () => ({ @@ -56,14 +56,11 @@ export const updateDomain = new mutationWithClientMutationId({ args, { i18n, - query, - language, - collections, - transaction, userKey, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByKey, loadOrgByKey }, + dataSources: { domain: domainDataSource, auditLogs, tags: tagsDS, organization: orgDS }, }, ) => { // Get User @@ -74,56 +71,30 @@ export const updateDomain = new mutationWithClientMutationId({ const { id: domainId } = fromGlobalId(cleanseInput(args.domainId)) const { id: orgId } = fromGlobalId(cleanseInput(args.orgId)) - const updatedDomain = cleanseInput(args.domain) - - let selectors - if (typeof args.selectors !== 'undefined') { - selectors = args.selectors.map((selector) => cleanseInput(selector)) - } else { - selectors = null - } let tags if (typeof args.tags !== 'undefined') { - tags = args.tags + tags = await tagsDS.byTagId.loadMany( + args.tags.map((tag) => { + return cleanseInput(tag) + }), + ) + tags = tags + .filter(({ visible, ownership, organizations }) => { + // Filter out tags that are not visible or do not belong to the org + return visible && (ownership === 'global' || organizations.some((org) => org === orgId)) + }) + .map((tag) => tag.tagId) } else { tags = null } - let archived - if (typeof args.archived !== 'undefined') { - archived = args.archived - } else { - archived = null - } - - let hidden - if (typeof args.hidden !== 'undefined') { - hidden = args.hidden - } else { - hidden = null - } - - let outsideComment - if (typeof args.outsideComment !== 'undefined') { - outsideComment = cleanseInput(args.outsideComment) - } else { - outsideComment = '' - } - - if (tags?.find(({ en }) => en === 'OUTSIDE')) { - if (outsideComment === '') { - console.warn(`User: ${userKey} attempted to create a domain with the OUTSIDE tag without providing a comment.`) - return { - _type: 'error', - code: 400, - description: i18n._(t`Please provide a comment when adding an outside domain.`), - } - } - } + const archived = typeof args.archived !== 'undefined' ? args.archived : null + const assetState = typeof args.assetState !== 'undefined' ? cleanseInput(args.assetState) : null + const cvdEnrollment = typeof args.cvdEnrollment !== 'undefined' ? args.cvdEnrollment : null // Check to see if domain exists - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) if (typeof domain === 'undefined') { console.warn( @@ -137,7 +108,7 @@ export const updateDomain = new mutationWithClientMutationId({ } // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn( @@ -153,7 +124,7 @@ export const updateDomain = new mutationWithClientMutationId({ // Check permission const permission = await checkPermission({ orgId: org._id }) - if (!['admin', 'owner', 'super_admin'].includes(permission)) { + if (!ac.can(permission).updateOwn('domain').granted) { console.warn( `User: ${userKey} attempted to update domain: ${domainId} for org: ${orgId}, however they do not have permission in that org.`, ) @@ -165,22 +136,18 @@ export const updateDomain = new mutationWithClientMutationId({ } // Check to see if org has a claim to this domain - let countCursor + let orgHasClaim try { - countCursor = await query` - WITH claims, domains, organizations - FOR v, e IN 1..1 ANY ${domain._id} claims - FILTER e._from == ${org._id} - RETURN e - ` - } catch (err) { - console.error( - `Database error occurred while user: ${userKey} attempted to update domain: ${domainId}, error: ${err}`, - ) + orgHasClaim = await domainDataSource.organizationHasClaim({ + orgId: org._id, + domainId: domain._id, + domainKey: domainId, + }) + } catch { throw new Error(i18n._(t`Unable to update domain. Please try again.`)) } - if (countCursor.count < 1) { + if (!orgHasClaim) { console.warn( `User: ${userKey} attempted to update domain: ${domainId} for org: ${orgId}, however that org has no claims to that domain.`, ) @@ -191,226 +158,87 @@ export const updateDomain = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - // Update domain - const domainToInsert = { - domain: updatedDomain.toLowerCase() || domain.domain.toLowerCase(), - lastRan: domain.lastRan, - archived: typeof archived !== 'undefined' ? archived : domain?.archived, + if ( + !ac.can(permission).updateOwn('cvd-enrollment').granted && + ['enrolled', 'deny'].includes(cvdEnrollment?.status) + ) { + console.warn( + `User: ${userKey} attempted to update the CVD enrollment for domain: ${domainId} in org: ${orgId}, however they do not have permission in that org.`, + ) + cvdEnrollment.status = cvdEnrollment.status === 'enrolled' ? 'pending' : 'not-enrolled' } - try { - await trx.step( - async () => - await query` - WITH domains - UPSERT { _key: ${domain._key} } - INSERT ${domainToInsert} - UPDATE ${domainToInsert} - IN domains - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to update domain: ${domainId}, error: ${err}`, + if (!ac.can(permission).updateAny('domain').granted && typeof args.highAvailability !== 'undefined') { + console.warn( + `User: ${userKey} attempted to update a high availability domain in: ${org.slug}, however they do not have permission to do so.`, ) - throw new Error(i18n._(t`Unable to update domain. Please try again.`)) + return { + _type: 'error', + code: 403, + description: i18n._(t`Permission Denied: Please contact super admin for help with updating domain.`), + } } - let claimCursor - try { - claimCursor = await query` - WITH claims - FOR claim IN claims - FILTER claim._from == ${org._id} && claim._to == ${domain._id} - RETURN MERGE({ id: claim._key, _type: "claim" }, claim) - ` - } catch (err) { - console.error(`Database error occurred when user: ${userKey} running loadDomainByKey: ${err}`) - } - let claim - try { - claim = await claimCursor.next() - } catch (err) { - console.error(`Cursor error occurred when user: ${userKey} running loadDomainByKey: ${err}`) + const claim = await domainDataSource.loadClaimByOrgAndDomain({ + orgId: org._id, + domainId: domain._id, + }) + + const domainToInsert = { + archived: typeof archived !== 'undefined' ? archived : domain?.archived, + ignoreRua: typeof args.ignoreRua !== 'undefined' ? args.ignoreRua : domain?.ignoreRua, + cvdEnrollment: typeof cvdEnrollment !== 'undefined' ? cvdEnrollment : domain?.cvdEnrollment, + highAvailability: typeof args.highAvailability !== 'undefined' ? args.highAvailability : domain?.highAvailability, } const claimToInsert = { tags: tags || claim?.tags, - hidden: typeof hidden !== 'undefined' ? hidden : claim?.hidden, firstSeen: typeof claim?.firstSeen === 'undefined' ? new Date().toISOString() : claim?.firstSeen, + assetState: assetState || claim?.assetState, } + let returnDomain try { - await trx.step( - async () => - await query` - WITH claims - UPSERT { _from: ${org._id}, _to: ${domain._id} } - INSERT ${claimToInsert} - UPDATE ${claimToInsert} - IN claims - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to update domain edge, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to update domain edge. Please try again.`)) - } - - if (selectors) { - // Get current selectors - let selectorCursor - try { - selectorCursor = await query` - FOR v, e IN 1..1 OUTBOUND ${domain._id} domainsToSelectors - RETURN v - ` - } catch (err) { - console.error(`Database error occurred when user: ${userKey} when getting current selectors: ${err}`) - } - - let oldSelectors - try { - oldSelectors = await selectorCursor.all() - } catch (err) { - console.error(`Cursor error occurred when user: ${userKey} when getting current selectors: ${err}`) - } - - // Remove old selector edges if no longer in use - const oldSelectorStrings = oldSelectors.map((selector) => selector.selector) - const selectorsToRemove = oldSelectors.filter((selector) => !selectors.includes(selector.selector)) - - if (selectorsToRemove.length > 0) { - try { - await trx.step( - () => - query` - FOR selector IN ${selectorsToRemove} - FOR v, e IN 1..1 ANY ${domain._id} domainsToSelectors - FILTER e._to == selector._id - REMOVE e IN domainsToSelectors - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to remove old selector edges, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to update domain. Please try again.`)) - } - } - - // Ensure new selectors are already in database, add new edges - const newSelectorsToAdd = selectors.filter((selector) => !oldSelectorStrings.includes(selector)) - - if (newSelectorsToAdd.length > 0) { - let newSelectorsCursor - try { - newSelectorsCursor = await trx.step( - () => - query` - FOR sel IN ${newSelectorsToAdd} - UPSERT { selector: sel } - INSERT { selector: sel } - UPDATE { } - IN selectors - RETURN NEW - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to insert new selectors, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to update domain. Please try again.`)) - } - - let newSelectors - try { - newSelectors = await newSelectorsCursor.all() - } catch (err) { - console.error(`Cursor error occurred when user: ${userKey} when getting new selectors: ${err}`) - } - - try { - await trx.step( - () => - query` - FOR selector IN ${newSelectors} - INSERT { _from: ${domain._id}, _to: selector._id } IN domainsToSelectors - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to insert new selector edges, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to update domain. Please try again.`)) - } - } - } - - // Commit transaction - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction commit error occurred when user: ${userKey} attempted to update domain: ${domainId}, error: ${err}`, - ) + const domainForUpdate = typeof domain?._key === 'undefined' ? { ...domain, _key: domainId } : domain + returnDomain = await domainDataSource.update({ domain: domainForUpdate, org, domainToInsert, claimToInsert }) + } catch { throw new Error(i18n._(t`Unable to update domain. Please try again.`)) } - // Clear dataloader and load updated domain - await loadDomainByKey.clear(domain._key) - const returnDomain = await loadDomainByKey.load(domain._key) - console.info(`User: ${userKey} successfully updated domain: ${domainId}.`) const updatedProperties = [] - if (domainToInsert.domain.toLowerCase() !== domain.domain.toLowerCase()) { + if (typeof assetState !== 'undefined' && assetState !== claim.assetState) { updatedProperties.push({ - name: 'domain', - oldValue: domain.domain, - newValue: domainToInsert.domain, + name: 'assetState', + oldValue: claim.assetState, + newValue: assetState, }) } - if ( - typeof selectors !== 'undefined' && - JSON.stringify(domainToInsert.selectors) !== JSON.stringify(domain.selectors) - ) { + + if (typeof cvdEnrollment !== 'undefined' && cvdEnrollment?.status !== domain?.cvdEnrollment?.status) { updatedProperties.push({ - name: 'selectors', - oldValue: domain.selectors, - newValue: selectors, + name: 'cvdEnrollment', + oldValue: JSON.stringify(domain.cvdEnrollment?.status), + newValue: JSON.stringify(cvdEnrollment.status), }) } - if (typeof tags !== 'undefined' && JSON.stringify(claim.tags) !== JSON.stringify(tags)) { + if (JSON.stringify(claim.tags) !== JSON.stringify(claimToInsert.tags)) { updatedProperties.push({ name: 'tags', oldValue: claim.tags, - newValue: tags, - }) - } - - if (typeof hidden !== 'undefined') { - updatedProperties.push({ - name: 'hidden', - oldValue: claim?.hidden, - newValue: hidden, + newValue: claimToInsert.tags, }) } if (updatedProperties.length > 0) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'update', target: { @@ -418,28 +246,25 @@ export const updateDomain = new mutationWithClientMutationId({ organization: { id: org._key, name: org.name, - }, // name of resource being acted upon - resourceType: 'domain', // user, org, domain + }, + resourceType: 'domain', updatedProperties, }, - reason: outsideComment !== '' ? outsideComment : null, }) } if (typeof archived !== 'undefined') { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'update', target: { resource: domain.domain, - resourceType: 'domain', // user, org, domain + resourceType: 'domain', updatedProperties: [{ name: 'archived', oldValue: domain.archived, newValue: archived }], }, }) @@ -449,10 +274,8 @@ export const updateDomain = new mutationWithClientMutationId({ return { ...returnDomain, - claimTags: claimToInsert.tags.map((tag) => { - return tag[language] - }), - hidden, + claimTags: claimToInsert.tags, + assetState, } }, }) diff --git a/api/src/domain/mutations/update-domains-by-domain-ids.js b/api/src/domain/mutations/update-domains-by-domain-ids.js new file mode 100644 index 000000000..65ad72b89 --- /dev/null +++ b/api/src/domain/mutations/update-domains-by-domain-ids.js @@ -0,0 +1,151 @@ +import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' +import { bulkModifyDomainsUnion } from '../unions' +import { GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString } from 'graphql' +import { t } from '@lingui/macro' +import ac from '../../access-control' + +export const updateDomainsByDomainIds = new mutationWithClientMutationId({ + name: 'UpdateDomainsByDomainIds', + description: '', + inputFields: () => ({ + orgId: { + type: new GraphQLNonNull(GraphQLID), + description: 'The global id of the organization you wish to assign this domain to.', + }, + tags: { + type: new GraphQLNonNull(new GraphQLList(GraphQLString)), + description: 'List of labelled tags users have applied to the domain.', + }, + domainIds: { + type: new GraphQLList(GraphQLID), + description: '', + }, + }), + outputFields: () => ({ + result: { + type: bulkModifyDomainsUnion, + description: '`BulkModifyDomainsUnion` returning either a `DomainBulkResult`, or `DomainErrorType` object.', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { + i18n, + userKey, + request: { ip }, + auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, + dataSources: { domain: domainDS, auditLogs, tags: tagsDS, organization: orgDS }, + validators: { cleanseInput }, + }, + ) => { + // Get User + const user = await userRequired() + verifiedRequired({ user }) + tfaRequired({ user }) + + // Cleanse input + const { id: orgId } = fromGlobalId(cleanseInput(args.orgId)) + let tags = (await tagsDS.byTagId.loadMany(args.tags.map((tag) => cleanseInput(tag)))) ?? [] + tags = tags + .filter( + ({ visible, ownership, organizations }) => + visible && (ownership === 'global' || organizations.some((org) => org === orgId)), + ) + .map((tag) => tag.tagId) + + // Check to see if org exists + const org = await orgDS.byKey.load(orgId) + if (typeof org === 'undefined') { + console.warn(`User: ${userKey} attempted to update domains to an organization: ${orgId} that does not exist.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to update domains in unknown organization.`), + } + } + + // Check to see if user belongs to org + const permission = await checkPermission({ orgId: org._id }) + if (!ac.can(permission).updateOwn('domain').granted) { + console.warn( + `User: ${userKey} attempted to update domains in: ${org.slug}, however they do not have permission to do so.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Permission Denied: Please contact organization admin for help with updating domains.`), + } + } + + let domainCount = 0 + for (const id of args.domainIds) { + const { id: domainId } = fromGlobalId(cleanseInput(id)) + // check for valid domain/claim + let checkClaim = await domainDS.loadClaimForOrgByDomainKey({ + orgId: org._id, + domainKey: domainId, + }) + + if (typeof checkClaim === 'undefined') { + const domain = await domainDS.byKey.load(domainId) + if (typeof domain !== 'undefined') { + const orgHasClaim = await domainDS.organizationHasClaim({ + orgId: org._id, + domainId: domain._id, + domainKey: domainId, + }) + if (orgHasClaim) { + const claim = await domainDS.loadClaimByOrgAndDomain({ orgId: org._id, domainId: domain._id }) + if (typeof claim !== 'undefined') { + checkClaim = { claim, domain: domain.domain } + } + } + } + } + + if (typeof checkClaim === 'undefined') { + console.warn( + `User: ${userKey} attempted to update a domain for: ${org.slug}, however that org does not have that domain claimed.`, + ) + continue + } + + const { claim, domain } = checkClaim + const claimToInsert = { + tags: [...new Set([...claim.tags, ...tags])], + } + + try { + await domainDS.updateClaim({ claim, claimToInsert }) + } catch (err) { + continue + } + + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: permission, + ipAddress: ip, + }, + action: 'update', + target: { + resource: domain, + updatedProperties: [{ name: 'tags', oldValue: claim.tags, newValue: claimToInsert.tags }], + organization: { + id: org._key, + name: org.name, + }, + resourceType: 'domain', + }, + }) + domainCount++ + } + + return { + _type: 'result', + status: i18n._(t`Successfully updated ${domainCount} domain(s) in ${org.slug} with ${tags.join(', ')}.`), + } + }, +}) diff --git a/api/src/domain/mutations/update-domains-by-filters.js b/api/src/domain/mutations/update-domains-by-filters.js new file mode 100644 index 000000000..dc1f7413f --- /dev/null +++ b/api/src/domain/mutations/update-domains-by-filters.js @@ -0,0 +1,133 @@ +import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' +import { bulkModifyDomainsUnion } from '../unions' +import { GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString } from 'graphql' +import { t } from '@lingui/macro' +import { domainFilter } from '../inputs' +import ac from '../../access-control' + +export const updateDomainsByFilters = new mutationWithClientMutationId({ + name: 'UpdateDomainsByFilters', + description: '', + inputFields: () => ({ + orgId: { + type: new GraphQLNonNull(GraphQLID), + description: 'The global id of the organization you wish to assign this domain to.', + }, + tags: { + type: new GraphQLNonNull(new GraphQLList(GraphQLString)), + description: 'List of labelled tags users have applied to the domain.', + }, + filters: { + type: new GraphQLList(domainFilter), + description: '', + }, + search: { + type: GraphQLString, + description: '', + }, + }), + outputFields: () => ({ + result: { + type: bulkModifyDomainsUnion, + description: '`BulkModifyDomainsUnion` returning either a `DomainBulkResult`, or `DomainErrorType` object.', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { + i18n, + userKey, + request: { ip }, + auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, + dataSources: { domain: domainDS, auditLogs, tags: tagsDS, organization: orgDS }, + validators: { cleanseInput }, + }, + ) => { + // Get User + const user = await userRequired() + verifiedRequired({ user }) + tfaRequired({ user }) + + // Cleanse input + const { id: orgId } = fromGlobalId(cleanseInput(args.orgId)) + const search = cleanseInput(args.search) + let tags = (await tagsDS.byTagId.loadMany(args.tags.map((tag) => cleanseInput(tag)))) ?? [] + tags = tags + .filter( + ({ visible, ownership, organizations }) => + visible && (ownership === 'global' || organizations.some((org) => org === orgId)), + ) + .map((tag) => tag.tagId) + const filters = args.filters + + // Check to see if org exists + const org = await orgDS.byKey.load(orgId) + if (typeof org === 'undefined') { + console.warn(`User: ${userKey} attempted to update domains to an organization: ${orgId} that does not exist.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to update domains in unknown organization.`), + } + } + + // Check to see if user belongs to org + const permission = await checkPermission({ orgId: org._id }) + if (!ac.can(permission).updateOwn('domain').granted) { + console.warn( + `User: ${userKey} attempted to update domains in: ${org.slug}, however they do not have permission to do so.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Permission Denied: Please contact organization admin for help with updating domains.`), + } + } + + const checkClaims = await domainDS.loadClaimsForOrgByFilters({ + orgId: org._id, + filters, + search, + }) + + let domainCount = 0 + for (const checkClaim of checkClaims) { + const { claim, domain } = checkClaim + const claimToInsert = { + tags: [...new Set([...claim.tags, ...tags])], + } + + try { + await domainDS.updateClaim({ claim, claimToInsert }) + } catch (err) { + continue + } + + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: permission, + ipAddress: ip, + }, + action: 'update', + target: { + resource: domain, + updatedProperties: [{ name: 'tags', oldValue: claim.tags, newValue: claimToInsert.tags }], + organization: { + id: org._key, + name: org.name, + }, + resourceType: 'domain', + }, + }) + domainCount++ + } + + return { + _type: 'result', + status: i18n._(t`Successfully updated ${domainCount} domain(s) in ${org.slug} with ${tags.join(', ')}.`), + } + }, +}) diff --git a/api/src/domain/objects/__tests__/domain.test.js b/api/src/domain/objects/__tests__/domain.test.js index e89cc4cbe..9e4f32ca8 100644 --- a/api/src/domain/objects/__tests__/domain.test.js +++ b/api/src/domain/objects/__tests__/domain.test.js @@ -12,6 +12,7 @@ import { Domain, Selectors } from '../../../scalars' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { dnsScanConnection } from '../../../dns-scan' +import { DmarcPhaseEnum } from '../../../enums' describe('given the domain object', () => { describe('testing its field definitions', () => { @@ -31,7 +32,7 @@ describe('given the domain object', () => { const demoType = domainType.getFields() expect(demoType).toHaveProperty('dmarcPhase') - expect(demoType.dmarcPhase.type).toMatchObject(GraphQLString) + expect(demoType.dmarcPhase.type).toMatchObject(DmarcPhaseEnum) }) it('has a hasDMARCReport field', () => { const demoType = domainType.getFields() @@ -87,6 +88,10 @@ describe('given the domain object', () => { expect(demoType).toHaveProperty('yearlyDmarcSummaries') expect(demoType.yearlyDmarcSummaries.type).toMatchObject(new GraphQLList(dmarcSummaryType)) }) + it('has a cvdEnrollment field', () => { + const demoType = domainType.getFields() + expect(demoType).toHaveProperty('cvdEnrollment') + }) }) describe('testing the field resolvers', () => { const consoleOutput = [] @@ -99,6 +104,37 @@ describe('given the domain object', () => { consoleOutput.length = 0 }) + describe('testing the cvdEnrollment resolver', () => { + it('returns the resolved value with correct structure when user is authenticated', async () => { + const demoType = domainType.getFields() + const mockUserRequired = jest.fn() + const cvdEnrollmentValue = { + status: 'ENROLLED', + description: 'Test asset', + maxSeverity: 'HIGH', + confidentialityRequirement: 'HIGH', + integrityRequirement: 'LOW', + availabilityRequirement: 'LOW', + } + + await expect( + demoType.cvdEnrollment.resolve( + { cvdEnrollment: cvdEnrollmentValue }, + {}, + { auth: { userRequired: mockUserRequired } }, + ), + ).resolves.toEqual(cvdEnrollmentValue) + expect(mockUserRequired).toHaveBeenCalled() + }) + it('returns undefined if cvdEnrollment is not present', async () => { + const demoType = domainType.getFields() + const mockUserRequired = jest.fn() + await expect( + demoType.cvdEnrollment.resolve({}, {}, { auth: { userRequired: mockUserRequired } }), + ).resolves.toBeUndefined() + }) + }) + describe('testing the id resolver', () => { it('returns the resolved value', () => { const demoType = domainType.getFields() @@ -179,15 +215,17 @@ describe('given the domain object', () => { expect( demoType.selectors.resolve( - { selectors }, + { _id: 'domains/1', selectors }, {}, { auth: { userRequired: jest.fn().mockReturnValue(true), - checkDomainPermission: jest.fn().mockReturnValue(true), }, - loaders: { - loadDkimSelectorsByDomainId: jest.fn().mockReturnValue(selectors), + dataSources: { + auth: { domainPermissionByDomainId: { load: jest.fn().mockResolvedValue(true) } }, + domain: { + dkimSelectorsByDomainId: jest.fn().mockReturnValue(selectors), + }, }, }, ), @@ -268,8 +306,10 @@ describe('given the domain object', () => { { _id: '1' }, { first: 1 }, { - loaders: { - loadOrgConnectionsByDomainId: jest.fn().mockReturnValue(expectedResult), + dataSources: { + organization: { + connectionsByDomainId: jest.fn().mockReturnValue(expectedResult), + }, }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(false), @@ -287,12 +327,11 @@ describe('given the domain object', () => { { _id: '1' }, { limit: 1 }, { - loaders: { - loadWebConnectionsByDomainId: jest.fn().mockReturnValue({ _id: '1', _key: '1' }), + dataSources: { + auth: { domainPermissionByDomainId: { load: jest.fn().mockResolvedValue(true) } }, + webScan: { getConnectionsByDomainId: jest.fn().mockReturnValue({ _id: '1', _key: '1' }) }, }, auth: { - checkDomainPermission: jest.fn().mockReturnValue(true), - checkSuperAdmin: jest.fn().mockReturnValue(false), userRequired: jest.fn().mockReturnValue(true), }, }, @@ -312,12 +351,11 @@ describe('given the domain object', () => { { _id: '1' }, { limit: 1 }, { - loaders: { - loadDnsConnectionsByDomainId: jest.fn().mockReturnValue({ _id: '1', _key: '1' }), + dataSources: { + auth: { domainPermissionByDomainId: { load: jest.fn().mockResolvedValue(true) } }, + dnsScan: { getConnectionsByDomainId: jest.fn().mockReturnValue({ _id: '1', _key: '1' }) }, }, auth: { - checkDomainPermission: jest.fn().mockReturnValue(true), - checkSuperAdmin: jest.fn().mockReturnValue(false), userRequired: jest.fn().mockReturnValue(true), }, }, diff --git a/api/src/domain/objects/domain.js b/api/src/domain/objects/domain.js index ae1018dc3..4bc535174 100644 --- a/api/src/domain/objects/domain.js +++ b/api/src/domain/objects/domain.js @@ -3,9 +3,9 @@ import { GraphQLBoolean, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectT import { connectionArgs, globalIdField } from 'graphql-relay' import { domainStatus } from './domain-status' -import { PeriodEnums } from '../../enums' +import { AssetStateEnums, PeriodEnums, DmarcPhaseEnum } from '../../enums' import { nodeInterface } from '../../node' -import { Domain, Selectors, Year } from '../../scalars' +import { CveID, Domain, Selectors, Year } from '../../scalars' import { dmarcSummaryType } from '../../dmarc-summaries/objects' import { dnsScanConnection } from '../../dns-scan/objects/dns-scan-connection' import { webConnection } from '../../web-scan/objects' @@ -14,7 +14,9 @@ import { organizationConnection } from '../../organization/objects' import { GraphQLDateTime } from 'graphql-scalars' import { dnsOrder } from '../../dns-scan/inputs' import { webOrder } from '../../web-scan/inputs/web-order' -import { mxRecordConnection } from '../../dns-scan/objects/mx-record-connection' +import { additionalFinding } from '../../additional-findings/objects/additional-finding' +import { tagType } from '../../tags/objects' +import { cvdEnrollment } from '../../additional-findings/objects' export const domainType = new GraphQLObjectType({ name: 'Domain', @@ -26,7 +28,7 @@ export const domainType = new GraphQLObjectType({ resolve: ({ domain }) => domain, }, dmarcPhase: { - type: GraphQLString, + type: DmarcPhaseEnum, description: 'The current dmarc phase the domain is compliant to.', resolve: ({ phase }) => phase, }, @@ -54,16 +56,16 @@ export const domainType = new GraphQLObjectType({ resolve: async ( { _id }, _, - { userKey, auth: { checkDomainPermission, userRequired }, loaders: { loadDkimSelectorsByDomainId } }, + { userKey, auth: { userRequired }, dataSources: { auth: authDS, domain: domainDataSource } }, ) => { await userRequired() - const permitted = await checkDomainPermission({ domainId: _id }) + const permitted = await authDS.domainPermissionByDomainId.load(_id) if (!permitted) { console.warn(`User: ${userKey} attempted to access selectors for ${_id}, but does not have permission.`) throw new Error(t`Cannot query domain selectors without permission.`) } - return await loadDkimSelectorsByDomainId({ + return await domainDataSource.dkimSelectorsByDomainId({ domainId: _id, }) }, @@ -88,6 +90,11 @@ export const domainType = new GraphQLObjectType({ type: GraphQLBoolean, resolve: ({ wildcardSibling }) => wildcardSibling, }, + wildcardEntry: { + description: 'Value that determines if a domain has a wildcard entry.', + type: GraphQLBoolean, + resolve: ({ wildcardEntry }) => wildcardEntry, + }, webScanPending: { description: 'Value that determines if a domain has a web scan pending.', type: GraphQLBoolean, @@ -115,10 +122,10 @@ export const domainType = new GraphQLObjectType({ ...connectionArgs, }, description: 'The organization that this domain belongs to.', - resolve: async ({ _id }, args, { auth: { checkSuperAdmin }, loaders: { loadOrgConnectionsByDomainId } }) => { + resolve: async ({ _id }, args, { auth: { checkSuperAdmin }, dataSources: { organization } }) => { const isSuperAdmin = await checkSuperAdmin() - return await loadOrgConnectionsByDomainId({ + return await organization.connectionsByDomainId({ domainId: _id, isSuperAdmin, ...args, @@ -147,13 +154,9 @@ export const domainType = new GraphQLObjectType({ ...connectionArgs, }, description: `DNS scan results.`, - resolve: async ( - { _id }, - args, - { userKey, auth: { checkDomainPermission, userRequired }, loaders: { loadDnsConnectionsByDomainId } }, - ) => { + resolve: async ({ _id }, args, { userKey, auth: { userRequired }, dataSources: { auth: authDS, dnsScan } }) => { await userRequired() - const permitted = await checkDomainPermission({ domainId: _id }) + const permitted = await authDS.domainPermissionByDomainId.load(_id) if (!permitted) { console.warn( `User: ${userKey} attempted to access dns scan results for ${_id}, but does not have permission.`, @@ -161,15 +164,15 @@ export const domainType = new GraphQLObjectType({ throw new Error(t`Cannot query dns scan results without permission.`) } - return await loadDnsConnectionsByDomainId({ + return await dnsScan.getConnectionsByDomainId({ domainId: _id, ...args, }) }, }, - mxRecordDiff: { - type: mxRecordConnection.connectionType, - description: 'List of MX record diffs for a given domain.', + web: { + type: webConnection.connectionType, + description: 'HTTPS, and TLS scan results.', args: { startDate: { type: GraphQLDateTime, @@ -180,22 +183,22 @@ export const domainType = new GraphQLObjectType({ description: 'End date for date filter.', }, orderBy: { - type: dnsOrder, - description: 'Ordering options for MX connections.', + type: webOrder, + description: 'Ordering options for web connections.', }, limit: { type: GraphQLInt, - description: 'Number of MX scans to retrieve.', + description: 'Number of web scans to retrieve.', + }, + excludePending: { + type: GraphQLBoolean, + description: `Exclude web scans which have pending status.`, }, ...connectionArgs, }, - resolve: async ( - { _id }, - args, - { userKey, auth: { checkDomainPermission, userRequired }, loaders: { loadMxRecordDiffByDomainId } }, - ) => { + resolve: async ({ _id }, args, { userKey, auth: { userRequired }, dataSources: { auth: authDS, webScan } }) => { await userRequired() - const permitted = await checkDomainPermission({ domainId: _id }) + const permitted = await authDS.domainPermissionByDomainId.load(_id) if (!permitted) { console.warn( `User: ${userKey} attempted to access web scan results for ${_id}, but does not have permission.`, @@ -203,58 +206,39 @@ export const domainType = new GraphQLObjectType({ throw new Error(t`Cannot query web scan results without permission.`) } - return await loadMxRecordDiffByDomainId({ + return await webScan.getConnectionsByDomainId({ domainId: _id, ...args, }) }, }, - web: { - type: webConnection.connectionType, - description: 'HTTPS, and TLS scan results.', - args: { - startDate: { - type: GraphQLDateTime, - description: 'Start date for date filter.', - }, - endDate: { - type: GraphQLDateTime, - description: 'End date for date filter.', - }, - orderBy: { - type: webOrder, - description: 'Ordering options for web connections.', - }, - limit: { - type: GraphQLInt, - description: 'Number of web scans to retrieve.', - }, - excludePending: { - type: GraphQLBoolean, - description: `Exclude web scans which have pending status.`, - }, - ...connectionArgs, - }, + additionalFindings: { + type: additionalFinding, + description: 'Additional findings imported from an external ASM tool.', resolve: async ( { _id }, - args, - { userKey, auth: { checkDomainPermission, userRequired }, loaders: { loadWebConnectionsByDomainId } }, + _, + { userKey, auth: { userRequired }, dataSources: { auth: authDS, additionalFindings } }, ) => { await userRequired() - const permitted = await checkDomainPermission({ domainId: _id }) + const permitted = await authDS.domainPermissionByDomainId.load(_id) if (!permitted) { console.warn( - `User: ${userKey} attempted to access web scan results for ${_id}, but does not have permission.`, + `User: ${userKey} attempted to access additional findings for domain: ${_id}, but does not have permission.`, ) - throw new Error(t`Cannot query web scan results without permission.`) + throw new Error(t`Cannot query additional findings without permission.`) } - return await loadWebConnectionsByDomainId({ + return await additionalFindings.getByDomainId({ domainId: _id, - ...args, }) }, }, + ignoredCves: { + type: new GraphQLList(CveID), + description: 'List of CVEs that have been ignored by the user.', + resolve: ({ ignoredCves }) => ignoredCves || [], + }, dmarcSummaryByPeriod: { description: 'Summarized DMARC aggregate reports.', args: { @@ -300,7 +284,7 @@ export const domainType = new GraphQLObjectType({ return { domainKey: _key, _id: dmarcSummaryEdge._to, - startDate: startDate, + startDate, } }, }, @@ -338,22 +322,66 @@ export const domainType = new GraphQLObjectType({ }, claimTags: { description: 'List of labelled tags users of an organization have applied to the claimed domain.', - type: new GraphQLList(GraphQLString), - resolve: ({ claimTags }) => claimTags, - }, - hidden: { - description: "Value that determines if a domain is excluded from an organization's results.", - type: GraphQLBoolean, - resolve: ({ hidden }) => hidden, + type: new GraphQLList(tagType), + args: { + isVisible: { + type: GraphQLBoolean, + description: 'Filter tags to only those that are visible.', + defaultValue: true, + }, + }, + resolve: async ({ claimTags }, args, { dataSources: { tags } }) => { + const loadedTags = await tags.byTagId.loadMany(claimTags) + return loadedTags.filter((tag) => { + return args.isVisible ? tag.visible : true + }) + }, }, userHasPermission: { description: 'Value that determines if a user is affiliated with a domain, whether through organization affiliation, verified organization network affiliation, or through super admin status.', type: GraphQLBoolean, - resolve: async ({ _id }, __, { auth: { checkDomainPermission } }) => { - return await checkDomainPermission({ - domainId: _id, - }) + resolve: async ({ _id }, __, { dataSources: { auth: authDS } }) => { + return await authDS.domainPermissionByDomainId.load(_id) + }, + }, + ignoreRua: { + description: 'Value that determines if a domain is ignoring rua reports.', + type: GraphQLBoolean, + resolve: ({ ignoreRua }) => ignoreRua, + }, + assetState: { + description: 'Value that determines if a domain is considered an asset.', + type: AssetStateEnums, + resolve: ({ assetState }) => assetState, + }, + hasEntrustCertificate: { + type: GraphQLBoolean, + description: `Whether or not the certificate chain contains an Entrust certificate.`, + resolve: ({ hasEntrustCertificate }) => hasEntrustCertificate, + }, + cveDetected: { + type: GraphQLBoolean, + description: `Whether or not a CVE has been detected in the domain's additional findings.`, + resolve: ({ cveDetected }) => cveDetected, + }, + cvdEnrollment: { + type: cvdEnrollment, + description: + 'The Coordinated Vulnerability Disclosure (CVD) enrollment status and requirements for this domain asset, including HackerOne integration details.', + resolve: async ({ cvdEnrollment }, __, { auth: { userRequired } }) => { + await userRequired() + + return cvdEnrollment + }, + }, + highAvailability: { + type: GraphQLBoolean, + description: 'Value that determines if the service is scanned for uptime.', + resolve: async ({ highAvailability }, __, { auth: { checkSuperAdmin } }) => { + const isSuperAdmin = await checkSuperAdmin() + if (isSuperAdmin) return highAvailability + return false }, }, }), diff --git a/api/src/domain/queries/__tests__/find-domain-by-domain.test.js b/api/src/domain/queries/__tests__/find-domain-by-domain.test.js index 02f664a2f..2c9399510 100644 --- a/api/src/domain/queries/__tests__/find-domain-by-domain.test.js +++ b/api/src/domain/queries/__tests__/find-domain-by-domain.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -8,8 +9,8 @@ import frenchMessages from '../../../locale/fr/messages' import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' -import { checkDomainPermission, userRequired, verifiedRequired } from '../../../auth' -import { loadDkimSelectorsByDomainId, loadDomainByDomain } from '../../loaders' +import { checkDomainPermission, userRequired, verifiedRequired, AuthDataSource } from '../../../auth' +import { DomainDataSource } from '../../data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' @@ -141,7 +142,7 @@ describe('given findDomainByDomain query', () => { contextValue: { i18n, userKey: user._key, - query: query, + query, auth: { loginRequiredBool: true, checkDomainPermission: checkDomainPermission({ @@ -154,19 +155,15 @@ describe('given findDomainByDomain query', () => { }), verifiedRequired: verifiedRequired({}), }, + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + domain: new DomainDataSource({ query, userKey: user._key }), + }, validators: { cleanseInput, }, loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), loadUserByKey: loadUserByKey({ query }), - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), }, }, }) @@ -235,7 +232,7 @@ describe('given findDomainByDomain query', () => { contextValue: { i18n, userKey: 1, - query: query, + query, auth: { checkDomainPermission: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ @@ -246,9 +243,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue(undefined), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue(undefined) }, }, }, }, @@ -297,9 +294,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ _id: '1' }), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue({ _id: '1' }) }, }, }, }, @@ -350,7 +347,7 @@ describe('given findDomainByDomain query', () => { contextValue: { i18n, userKey: 1, - query: query, + query, auth: { checkDomainPermission: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ @@ -361,9 +358,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue(undefined), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue(undefined) }, }, }, }, @@ -412,9 +409,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ _id: '1' }), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue({ _id: '1' }) }, }, }, }, diff --git a/api/src/domain/queries/__tests__/find-my-domains.test.js b/api/src/domain/queries/__tests__/find-my-domains.test.js index b43533afb..04f0729d7 100644 --- a/api/src/domain/queries/__tests__/find-my-domains.test.js +++ b/api/src/domain/queries/__tests__/find-my-domains.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -8,8 +9,8 @@ import frenchMessages from '../../../locale/fr/messages' import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' -import { checkDomainPermission, checkSuperAdmin, userRequired, verifiedRequired } from '../../../auth' -import { loadDkimSelectorsByDomainId, loadDomainConnectionsByUserId } from '../../loaders' +import { AuthDataSource, checkDomainPermission, checkSuperAdmin, userRequired, verifiedRequired } from '../../../auth' +import { DomainDataSource } from '../../data-source' import { loadUserByKey } from '../../../user' import dbschema from '../../../../database.json' @@ -55,7 +56,6 @@ describe('given findMyDomainsQuery', () => { user = await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'french', emailValidated: true, }) @@ -195,19 +195,14 @@ describe('given findMyDomainsQuery', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ - query, - userKey: user._key, - cleanseInput, - auth: { loginRequired: true }, - }), - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + dataSources: { + auth: new AuthDataSource({ query, userKey: user._key }), + domain: new DomainDataSource({ query, userKey: user._key, cleanseInput, i18n, - auth: { loginRequiredBool: true }, + loginRequiredBool: true, }), }, }, @@ -304,12 +299,12 @@ describe('given findMyDomainsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ + dataSources: { + domain: new DomainDataSource({ query: mockedQuery, userKey: 1, cleanseInput, - auth: { loginRequired: true }, + loginRequiredBool: true, i18n, }), }, @@ -379,12 +374,12 @@ describe('given findMyDomainsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ + dataSources: { + domain: new DomainDataSource({ query: mockedQuery, userKey: 1, cleanseInput, - auth: { loginRequired: true }, + loginRequiredBool: true, i18n, }), }, diff --git a/api/src/domain/queries/find-domain-by-domain.js b/api/src/domain/queries/find-domain-by-domain.js index ee896ae55..9532217cc 100644 --- a/api/src/domain/queries/find-domain-by-domain.js +++ b/api/src/domain/queries/find-domain-by-domain.js @@ -20,7 +20,7 @@ export const findDomainByDomain = { i18n, userKey, auth: { checkDomainPermission, userRequired, verifiedRequired, loginRequiredBool }, - loaders: { loadDomainByDomain }, + dataSources: { domain: domainDataSource }, validators: { cleanseInput }, }, ) => { @@ -34,7 +34,7 @@ export const findDomainByDomain = { const domainInput = cleanseInput(args.domain) // Retrieve domain by domain - const domain = await loadDomainByDomain.load(domainInput) + const domain = await domainDataSource.byDomain.load(domainInput) if (typeof domain === 'undefined') { console.warn(`User ${userKey} could not retrieve domain.`) diff --git a/api/src/domain/queries/find-my-domains.js b/api/src/domain/queries/find-my-domains.js index 33de5f650..5ba1c7d7c 100644 --- a/api/src/domain/queries/find-my-domains.js +++ b/api/src/domain/queries/find-my-domains.js @@ -1,8 +1,8 @@ -import {GraphQLBoolean, GraphQLString} from 'graphql' -import {connectionArgs} from 'graphql-relay' +import { GraphQLBoolean, GraphQLString, GraphQLList } from 'graphql' +import { connectionArgs } from 'graphql-relay' -import {domainOrder} from '../inputs' -import {domainConnection} from '../objects' +import { domainFilter, domainOrder } from '../inputs' +import { domainConnection } from '../objects' export const findMyDomains = { type: domainConnection.connectionType, @@ -14,13 +14,20 @@ export const findMyDomains = { }, ownership: { type: GraphQLBoolean, - description: - 'Limit domains to those that belong to an organization that has ownership.', + description: 'Limit domains to those that belong to an organization that has ownership.', }, search: { type: GraphQLString, description: 'String used to search for domains.', }, + isAffiliated: { + type: GraphQLBoolean, + description: 'Filter the results based on the users affiliation.', + }, + filters: { + type: new GraphQLList(domainFilter), + description: 'Filters used to limit domains returned.', + }, ...connectionArgs, }, resolve: async ( @@ -28,23 +35,18 @@ export const findMyDomains = { args, { userKey, - auth: { - checkSuperAdmin, - userRequired, - loginRequiredBool, - verifiedRequired, - }, - loaders: {loadDomainConnectionsByUserId}, + auth: { checkSuperAdmin, userRequired, loginRequiredBool, verifiedRequired }, + dataSources: { domain: domainDataSource }, }, ) => { if (loginRequiredBool) { const user = await userRequired() - verifiedRequired({user}) + verifiedRequired({ user }) } const isSuperAdmin = await checkSuperAdmin() - const domainConnections = await loadDomainConnectionsByUserId({ + const domainConnections = await domainDataSource.connectionsByUserId({ isSuperAdmin, ...args, }) diff --git a/api/src/domain/unions/ignore-cve-union.js b/api/src/domain/unions/ignore-cve-union.js new file mode 100644 index 000000000..aa3fba44f --- /dev/null +++ b/api/src/domain/unions/ignore-cve-union.js @@ -0,0 +1,16 @@ +import { GraphQLUnionType } from 'graphql' +import { domainErrorType, domainType } from '../objects' + +export const ignoreCveUnion = new GraphQLUnionType({ + name: 'IgnoreCveUnion', + description: `This union is used with the \`IgnoreCve\` mutation, +allowing for users to ignore a CVE for a domain and support any errors that may occur`, + types: [domainErrorType, domainType], + resolveType({ _type }) { + if (_type === 'domain') { + return domainType.name + } else { + return domainErrorType.name + } + }, +}) diff --git a/api/src/enums/affiliation-user-order-field.js b/api/src/enums/affiliation-user-order-field.js index 6efe1b53c..c7cc3a41f 100644 --- a/api/src/enums/affiliation-user-order-field.js +++ b/api/src/enums/affiliation-user-order-field.js @@ -1,28 +1,20 @@ -import {GraphQLEnumType} from 'graphql' +import { GraphQLEnumType } from 'graphql' export const AffiliationUserOrderField = new GraphQLEnumType({ name: 'AffiliationUserOrderField', description: 'Properties by which affiliation connections can be ordered.', values: { - USER_USERNAME: { - value: 'user-username', - description: 'Order affiliation edges by username.', + USERNAME: { + value: 'username', + description: 'Order affiliations by username.', }, - USER_DISPLAYNAME: { - value: 'user-displayName', - description: 'Order affiliation edges by displayName.', + DISPLAY_NAME: { + value: 'display_name', + description: 'Order affiliations by display name.', }, - USER_EMAIL_VALIDATED: { - value: 'user-emailValidated', - description: 'Order affiliation edges by user verification status.', - }, - USER_INSIDER: { - value: 'user-insider', - description: 'Order affiliation edges by user insider status.', - }, - USER_AFFILIATIONS_COUNT: { - value: 'user-affiliations-totalCount', - description: 'Order affiliation edges by amount of total affiliations.', + PERMISSION: { + value: 'permission', + description: 'Order affiliations by permission.', }, }, }) diff --git a/api/src/enums/asset-state.js b/api/src/enums/asset-state.js new file mode 100644 index 000000000..6fa8921c9 --- /dev/null +++ b/api/src/enums/asset-state.js @@ -0,0 +1,28 @@ +import { GraphQLEnumType } from 'graphql' + +export const AssetStateEnums = new GraphQLEnumType({ + name: 'AssetStateEnums', + values: { + APPROVED: { + value: 'approved', + description: 'An asset confirmed to belong to the organization.', + }, + DEPENDENCY: { + value: 'dependency', + description: 'An asset that is owned by a third party and supports the operation of organization-owned assets.', + }, + MONITOR_ONLY: { + value: 'monitor-only', + description: 'An asset that is relevant to the organization but is not a direct part of the attack surface.', + }, + CANDIDATE: { + value: 'candidate', + description: 'An asset that is suspected to belong to the organization but has not been confirmed.', + }, + REQUIRES_INVESTIGATION: { + value: 'requires-investigation', + description: 'An asset that requires further investigation to determine its relationship to the organization.', + }, + }, + description: "An enum used to describe how an asset relates to an organization's attack surface.", +}) diff --git a/api/src/enums/chart-summary-scope.js b/api/src/enums/chart-summary-scope.js new file mode 100644 index 000000000..cac09dbd9 --- /dev/null +++ b/api/src/enums/chart-summary-scope.js @@ -0,0 +1,24 @@ +import { GraphQLEnumType } from 'graphql' + +export const ChartSummaryScopeEnums = new GraphQLEnumType({ + name: 'ChartSummaryScopeEnums', + values: { + ALL: { + value: 'all', + description: 'Summary covering all organizations holding an approved claim.', + }, + VERIFIED: { + value: 'verified', + description: 'Summary covering verified organizations.', + }, + PSD: { + value: 'psd', + description: 'Summary covering organizations subject to the Policy on Service and Digital.', + }, + PGS: { + value: 'pgs', + description: 'Summary covering organizations subject to the Policy on Government Security.', + }, + }, + description: 'An enum used to select which set of organizations a chart summary covers.', +}) \ No newline at end of file diff --git a/api/src/enums/cvd-requirement.js b/api/src/enums/cvd-requirement.js new file mode 100644 index 000000000..d37aa1b9c --- /dev/null +++ b/api/src/enums/cvd-requirement.js @@ -0,0 +1,20 @@ +import { GraphQLEnumType } from 'graphql' + +export const CvdRequirementEnums = new GraphQLEnumType({ + name: 'CvdRequirementEnums', + values: { + NONE: { + value: 'none', + description: 'No additional CVSS environmental requirement for this asset.', + }, + LOW: { + value: 'low', + description: 'Low CVSS environmental requirement for this asset.', + }, + HIGH: { + value: 'high', + description: 'High CVSS environmental requirement for this asset.', + }, + }, + description: 'Enumerates the CVSS environmental requirement levels for CVD-enrolled assets.', +}) diff --git a/api/src/enums/dmarc-phase.js b/api/src/enums/dmarc-phase.js new file mode 100644 index 000000000..ca7694c6e --- /dev/null +++ b/api/src/enums/dmarc-phase.js @@ -0,0 +1,24 @@ +import { GraphQLEnumType } from 'graphql' + +export const DmarcPhaseEnum = new GraphQLEnumType({ + name: 'DmarcPhaseEnum', + description: 'Phases of DMARC implementation.', + values: { + ASSESS: { + value: 'assess', + description: 'Assess domains and DMARC status.', + }, + DEPLOY: { + value: 'deploy', + description: 'Deploy SPF and DKIM records.', + }, + ENFORCE: { + value: 'enforce', + description: 'Enforce DMARC policies.', + }, + MAINTAIN: { + value: 'maintain', + description: 'Maintain DMARC and update records.', + }, + }, +}) diff --git a/api/src/enums/domain-filter-category.js b/api/src/enums/domain-filter-category.js new file mode 100644 index 000000000..bc2232598 --- /dev/null +++ b/api/src/enums/domain-filter-category.js @@ -0,0 +1,29 @@ +import { GraphQLEnumType } from 'graphql' +import { DomainOrderField } from './domain-order-field' + +export const DomainFilterCategory = new GraphQLEnumType({ + name: 'DomainFilterCategory', + description: 'Properties by which domain connections can be filtered.', + values: { + ...DomainOrderField.getValues().reduce((acc, { name, value, description }) => { + acc[name] = { value, description } + return acc + }, {}), + TAGS: { + value: 'tags', + description: 'Order domains by tags.', + }, + ASSET_STATE: { + value: 'asset-state', + description: 'Order domains by asset state.', + }, + GUIDANCE_TAG: { + value: 'guidance-tag', + description: 'Scanner findings.', + }, + DMARC_PHASE: { + value: 'dmarc-phase', + description: 'DMARC phase.', + }, + }, +}) diff --git a/api/src/enums/domain-order-field.js b/api/src/enums/domain-order-field.js index 98ec17b49..ea8c60b80 100644 --- a/api/src/enums/domain-order-field.js +++ b/api/src/enums/domain-order-field.js @@ -48,9 +48,5 @@ export const DomainOrderField = new GraphQLEnumType({ value: 'spf-status', description: 'Order domains by spf status.', }, - TAGS: { - value: 'tags', - description: 'Order domains by tags.', - }, }, }) diff --git a/api/src/enums/domain-tag-label.js b/api/src/enums/domain-tag-label.js index 896d61102..6ae43ca99 100644 --- a/api/src/enums/domain-tag-label.js +++ b/api/src/enums/domain-tag-label.js @@ -3,46 +3,6 @@ import { GraphQLEnumType } from 'graphql' export const DomainTagLabel = new GraphQLEnumType({ name: 'DomainTagLabel', values: { - NEW: { - value: 'NEW', - description: 'English label for tagging domains as new to the system.', - }, - NOUVEAU: { - value: 'NOUVEAU', - description: 'French label for tagging domains as new to the system.', - }, - PROD: { - value: 'PROD', - description: 'Bilingual Label for tagging domains as a production environment.', - }, - STAGING: { - value: 'STAGING', - description: 'English label for tagging domains as a staging environment.', - }, - DEV: { - value: 'DÉV', - description: 'French label for tagging domains as a staging environment.', - }, - TEST: { - value: 'TEST', - description: 'Bilingual label for tagging domains as a test environment.', - }, - WEB: { - value: 'WEB', - description: 'Bilingual label for tagging domains as web-hosting.', - }, - INACTIVE: { - value: 'INACTIVE', - description: 'English label for tagging domains that are not active.', - }, - INACTIF: { - value: 'INACTIF', - description: 'French label for tagging domains that are not active.', - }, - HIDDEN: { - value: 'hidden', - description: 'English label for tagging domains that are hidden.', - }, ARCHIVED: { value: 'archived', description: 'English label for tagging domains that are archived.', @@ -59,17 +19,30 @@ export const DomainTagLabel = new GraphQLEnumType({ value: 'wildcard-sibling', description: 'Label for tagging domains that have a wildcard sibling.', }, + WILDCARD_ENTRY: { + value: 'wildcard-entry', + description: 'Label for tagging domains that have a wildcard entry.', + }, SCAN_PENDING: { value: 'scan-pending', description: 'Label for tagging domains that have a pending web scan.', }, - OUTSIDE: { - value: 'OUTSIDE', - description: 'English label for tagging domains that are outside the scope of the project.', + CVE_DETECTED: { + value: 'cve-detected', + description: 'Label for tagging domains that have vulnerabilities.', + }, + CVD_ENROLLED: { + value: 'cvd-enrolled', + description: 'Label for tagging domains that are enrolled in the Coordinated Vulnerability Disclosure program.', + }, + CVD_PENDING: { + value: 'cvd-pending', + description: + 'Label for tagging domains that are pending enrollment in the Coordinated Vulnerability Disclosure program.', }, - EXTERIEUR: { - value: 'EXTÉRIEUR', - description: 'French label for tagging domains that are outside the scope of the project.', + CVD_DENY: { + value: 'cvd-deny', + description: 'Label for tagging domains that have been explicitly excluded from the Coordinated Vulnerability Disclosure program.', }, }, description: 'An enum used to assign and test user-generated domain tags', diff --git a/api/src/enums/enrollment-status.js b/api/src/enums/enrollment-status.js new file mode 100644 index 000000000..1ab1c2a33 --- /dev/null +++ b/api/src/enums/enrollment-status.js @@ -0,0 +1,24 @@ +import { GraphQLEnumType } from 'graphql' + +export const EnrollmentStatusEnums = new GraphQLEnumType({ + name: 'EnrollmentStatusEnums', + values: { + ENROLLED: { + value: 'enrolled', + description: 'The asset is enrolled in the CVD program and eligible for coordinated vulnerability disclosure.', + }, + PENDING: { + value: 'pending', + description: 'The asset enrollment is pending approval for the CVD program.', + }, + NOT_ENROLLED: { + value: 'not-enrolled', + description: 'The asset is not enrolled in the CVD program.', + }, + DENY: { + value: 'deny', + description: 'The asset has been explicitly excluded from the CVD program.', + }, + }, + description: 'Enumerates the possible enrollment states for the Coordinated Vulnerability Disclosure (CVD) program.', +}) diff --git a/api/src/enums/guidance-tag-order-field.js b/api/src/enums/guidance-tag-order-field.js index 68ef6d3bc..8883a0f16 100644 --- a/api/src/enums/guidance-tag-order-field.js +++ b/api/src/enums/guidance-tag-order-field.js @@ -1,4 +1,4 @@ -import {GraphQLEnumType} from 'graphql' +import { GraphQLEnumType } from 'graphql' export const GuidanceTagOrderField = new GraphQLEnumType({ name: 'GuidanceTagOrderField', @@ -16,5 +16,9 @@ export const GuidanceTagOrderField = new GraphQLEnumType({ value: 'guidance', description: 'Order guidance tag edges by tag guidance.', }, + TAG_COUNT: { + value: 'tag-count', + description: 'Order guidance tag edges by tag count.', + }, }, }) diff --git a/api/src/enums/index.js b/api/src/enums/index.js index 6f478eefd..5086e6b0d 100644 --- a/api/src/enums/index.js +++ b/api/src/enums/index.js @@ -1,5 +1,8 @@ export * from './affiliation-org-order-field' export * from './affiliation-user-order-field' +export * from './asset-state' +export * from './chart-summary-scope' +export * from './user-order-field' export * from './dkim-order-field' export * from './dkim-result-order-field' export * from './dmarc-order-field' @@ -27,4 +30,9 @@ export * from './tfa-send-method' export * from './verified-domain-order-field' export * from './verified-organization-order-field' export * from './web-order-field' -export * from './outside-domain-comment' +export * from './tag-ownership' +export * from './system-filter-value' +export * from './domain-filter-category' +export * from './dmarc-phase' +export * from './enrollment-status' +export * from './cvd-requirement' diff --git a/api/src/enums/outside-domain-comment.js b/api/src/enums/outside-domain-comment.js deleted file mode 100644 index ccc7ba7fc..000000000 --- a/api/src/enums/outside-domain-comment.js +++ /dev/null @@ -1,20 +0,0 @@ -import { GraphQLEnumType } from 'graphql' - -export const OutsideDomainCommentEnum = new GraphQLEnumType({ - name: 'OutsideDomainCommentEnum', - values: { - INVESTMENT: { - value: 'investment', - description: 'Organization is invested in the outside domain.', - }, - OWNERSHIP: { - value: 'ownership', - description: 'Organization owns this domain, but it is outside the allowed scope.', - }, - OTHER: { - value: 'other', - description: 'Other reason.', - }, - }, - description: 'Reason why an outside domain was added to the organization.', -}) diff --git a/api/src/enums/resource-type.js b/api/src/enums/resource-type.js index c24ce4473..6b37fe045 100644 --- a/api/src/enums/resource-type.js +++ b/api/src/enums/resource-type.js @@ -15,6 +15,10 @@ export const ResourceTypeEnums = new GraphQLEnumType({ value: 'domain', description: 'A domain affiliated with an organization.', }, + TAG: { + value: 'tag', + description: 'A tag used for organizing domains.', + }, }, description: 'Keywords used to describe resources that can be modified.', }) diff --git a/api/src/enums/roles.js b/api/src/enums/roles.js index 61080e6fb..a9878aaaa 100644 --- a/api/src/enums/roles.js +++ b/api/src/enums/roles.js @@ -25,6 +25,10 @@ export const RoleEnums = new GraphQLEnumType({ value: 'super_admin', description: 'A user who has the same access as an admin, but can define new admins.', }, + SERVICE: { + value: 'service', + description: 'An internal service used by Tracker to make changes to organizational data.', + }, }, description: 'An enum used to assign, and test users roles.', }) diff --git a/api/src/enums/system-filter-value.js b/api/src/enums/system-filter-value.js new file mode 100644 index 000000000..1d4f10f5b --- /dev/null +++ b/api/src/enums/system-filter-value.js @@ -0,0 +1,23 @@ +import { GraphQLEnumType } from 'graphql' +import { StatusEnum } from './status' +import { DomainTagLabel } from './domain-tag-label' +import { AssetStateEnums } from './asset-state' +import { DmarcPhaseEnum } from './dmarc-phase' + +const getEnumValues = (enums) => { + return enums.getValues().reduce((acc, { name, value, description }) => { + acc[name] = { value, description } + return acc + }, {}) +} + +export const filterEnum = new GraphQLEnumType({ + name: 'SystemFilterValue', + values: { + ...getEnumValues(StatusEnum), + ...getEnumValues(DomainTagLabel), + ...getEnumValues(AssetStateEnums), + ...getEnumValues(DmarcPhaseEnum), + }, + description: 'Filter value from system-defined statuses or tags.', +}) diff --git a/api/src/enums/tag-ownership.js b/api/src/enums/tag-ownership.js new file mode 100644 index 000000000..90e7ada99 --- /dev/null +++ b/api/src/enums/tag-ownership.js @@ -0,0 +1,20 @@ +import { GraphQLEnumType } from 'graphql' + +export const TagOwnershipEnums = new GraphQLEnumType({ + name: 'TagOwnershipEnums', + values: { + GLOBAL: { + value: 'global', + description: 'Visible to all users, not affiliated with any organization.', + }, + ORG: { + value: 'org', + description: 'Visible to all users affiliated with an organization.', + }, + PENDING: { + value: 'pending', + description: 'Visible to admins, but not yet affiliated with any organization.', + }, + }, + description: 'Enum representing the ownership of a tag, determining its visibility and affiliation.', +}) diff --git a/api/src/enums/user-order-field.js b/api/src/enums/user-order-field.js new file mode 100644 index 000000000..e4f428e9c --- /dev/null +++ b/api/src/enums/user-order-field.js @@ -0,0 +1,28 @@ +import { GraphQLEnumType } from 'graphql' + +export const UserOrderField = new GraphQLEnumType({ + name: 'UserOrderField', + description: 'Properties by which affiliation connections can be ordered.', + values: { + USER_USERNAME: { + value: 'user-username', + description: 'Order affiliation edges by username.', + }, + USER_DISPLAYNAME: { + value: 'user-displayName', + description: 'Order affiliation edges by displayName.', + }, + USER_EMAIL_VALIDATED: { + value: 'user-emailValidated', + description: 'Order affiliation edges by user verification status.', + }, + USER_INSIDER: { + value: 'user-insider', + description: 'Order affiliation edges by user insider status.', + }, + USER_AFFILIATIONS_COUNT: { + value: 'user-affiliations-totalCount', + description: 'Order affiliation edges by amount of total affiliations.', + }, + }, +}) diff --git a/api/src/guidance-tag/data-source.js b/api/src/guidance-tag/data-source.js new file mode 100644 index 000000000..913b78e36 --- /dev/null +++ b/api/src/guidance-tag/data-source.js @@ -0,0 +1,35 @@ +import { + loadAggregateGuidanceTagByTagId, + loadAggregateGuidanceTagConnectionsByTagId, + loadDkimGuidanceTagByTagId, + loadDkimGuidanceTagConnectionsByTagId, + loadDmarcGuidanceTagByTagId, + loadDmarcGuidanceTagConnectionsByTagId, + loadGuidanceTagByTagId, + loadGuidanceTagSummaryConnectionsByTagId, + loadHttpsGuidanceTagByTagId, + loadHttpsGuidanceTagConnectionsByTagId, + loadSpfGuidanceTagByTagId, + loadSpfGuidanceTagConnectionsByTagId, + loadSslGuidanceTagByTagId, + loadSslGuidanceTagConnectionsByTagId, +} from './loaders' + +export class GuidanceTagDataSource { + constructor({ query, userKey, i18n, language, cleanseInput }) { + this.byTagId = loadGuidanceTagByTagId({ query, userKey, i18n, language }) + this.summaryConnectionsByTagId = loadGuidanceTagSummaryConnectionsByTagId({ query, userKey, cleanseInput, i18n, language }) + this.aggregateByTagId = loadAggregateGuidanceTagByTagId({ query, userKey, i18n, language }) + this.aggregateConnectionsByTagId = loadAggregateGuidanceTagConnectionsByTagId({ query, userKey, cleanseInput, i18n, language }) + this.dkimByTagId = loadDkimGuidanceTagByTagId({ query, userKey, i18n, language }) + this.dkimConnectionsByTagId = loadDkimGuidanceTagConnectionsByTagId({ query, userKey, cleanseInput, i18n, language }) + this.dmarcByTagId = loadDmarcGuidanceTagByTagId({ query, userKey, i18n, language }) + this.dmarcConnectionsByTagId = loadDmarcGuidanceTagConnectionsByTagId({ query, userKey, cleanseInput, i18n, language }) + this.httpsByTagId = loadHttpsGuidanceTagByTagId({ query, userKey, i18n, language }) + this.httpsConnectionsByTagId = loadHttpsGuidanceTagConnectionsByTagId({ query, userKey, cleanseInput, i18n, language }) + this.spfByTagId = loadSpfGuidanceTagByTagId({ query, userKey, i18n, language }) + this.spfConnectionsByTagId = loadSpfGuidanceTagConnectionsByTagId({ query, userKey, cleanseInput, i18n, language }) + this.sslByTagId = loadSslGuidanceTagByTagId({ query, userKey, i18n, language }) + this.sslConnectionsByTagId = loadSslGuidanceTagConnectionsByTagId({ query, userKey, cleanseInput, i18n, language }) + } +} diff --git a/api/src/guidance-tag/index.js b/api/src/guidance-tag/index.js index 6d2a5b587..52de3be3a 100644 --- a/api/src/guidance-tag/index.js +++ b/api/src/guidance-tag/index.js @@ -1,3 +1,4 @@ +export * from './data-source' export * from './inputs' export * from './loaders' export * from './objects' diff --git a/api/src/guidance-tag/loaders/__tests__/load-guidance-tags-connections.test.js b/api/src/guidance-tag/loaders/__tests__/load-guidance-tags-connections.test.js new file mode 100644 index 000000000..a2fafde69 --- /dev/null +++ b/api/src/guidance-tag/loaders/__tests__/load-guidance-tags-connections.test.js @@ -0,0 +1,77 @@ +const { loadGuidanceTagSummaryConnectionsByTagId } = require('../load-guidance-tags-connections') +const { createI18n } = require('../../../create-i18n') + +describe('loadGuidanceTagSummaryConnectionsByTagId', () => { + let query, userKey, cleanseInput, language + + const i18n = createI18n('en') + + beforeEach(() => { + query = jest.fn() + userKey = '1' + cleanseInput = jest.fn((input) => input) + language = 'en' + }) + + it('successfully retrieves data', async () => { + const guidanceTags = { tag1: 3, tag2: 5 } + const orderBy = { field: 'tag-id', direction: 'ASC' } + + query.mockResolvedValueOnce({ + next: jest.fn().mockResolvedValueOnce({ + guidanceTags: [ + { _key: '1', tagName: 'Tag 1' }, + { _key: '2', tagName: 'Tag 2' }, + ], + totalCount: 2, + }), + }) + + const loader = loadGuidanceTagSummaryConnectionsByTagId({ + query, + userKey, + cleanseInput, + i18n, + language, + }) + + const result = await loader({ guidanceTags, orderBy }) + expect(result).toEqual({ + guidanceTags: [ + { _key: '1', tagName: 'Tag 1' }, + { _key: '2', tagName: 'Tag 2' }, + ], + totalCount: 2, + }) + }) + + it('throws an error if there is a database error', async () => { + query.mockRejectedValueOnce(new Error('Database error')) + + const loader = loadGuidanceTagSummaryConnectionsByTagId({ + query, + userKey, + cleanseInput, + i18n, + language, + }) + + await expect(loader({ guidanceTags: {} })).rejects.toThrow('Unable to load guidance tag(s). Please try again.') + }) + + it('throws an error if there is a cursor error', async () => { + query.mockResolvedValueOnce({ + next: jest.fn().mockRejectedValueOnce(new Error('Cursor error')), + }) + + const loader = loadGuidanceTagSummaryConnectionsByTagId({ + query, + userKey, + cleanseInput, + i18n, + language, + }) + + await expect(loader({ guidanceTags: {} })).rejects.toThrow('Unable to load guidance tag(s). Please try again.') + }) +}) diff --git a/api/src/guidance-tag/loaders/index.js b/api/src/guidance-tag/loaders/index.js index ea70fd106..88a9f3484 100644 --- a/api/src/guidance-tag/loaders/index.js +++ b/api/src/guidance-tag/loaders/index.js @@ -10,3 +10,5 @@ export * from './load-spf-guidance-tags' export * from './load-spf-guidance-tags-connections' export * from './load-ssl-guidance-tags' export * from './load-ssl-guidance-tags-connections' +export * from './load-guidance-tags' +export * from './load-guidance-tags-connections' diff --git a/api/src/guidance-tag/loaders/load-guidance-tags-connections.js b/api/src/guidance-tag/loaders/load-guidance-tags-connections.js new file mode 100644 index 000000000..ecb08d0c1 --- /dev/null +++ b/api/src/guidance-tag/loaders/load-guidance-tags-connections.js @@ -0,0 +1,75 @@ +import { aql } from 'arangojs' +import { t } from '@lingui/macro' + +export const loadGuidanceTagSummaryConnectionsByTagId = + ({ query, userKey, i18n, language }) => + async ({ guidanceTags, orderBy }) => { + const tagIds = Object.keys(guidanceTags) + + let sortByField = aql`` + if (typeof orderBy !== 'undefined') { + /* istanbul ignore else */ + if (orderBy.field === 'tag-id') { + sortByField = aql`SORT tag._key ${orderBy.direction}` + } else if (orderBy.field === 'tag-name') { + sortByField = aql`SORT TRANSLATE(${language}, tag).tagName ${orderBy.direction}` + } else if (orderBy.field === 'guidance') { + sortByField = aql`SORT TRANSLATE(${language}, tag).guidance ${orderBy.direction}` + } else if (orderBy.field === 'tag-count') { + sortByField = aql`SORT TRANSLATE(tag._key, tagSummaries) ${orderBy.direction}` + } + } + + let guidanceTagInfoCursor + try { + guidanceTagInfoCursor = await query` + WITH guidanceTags + LET tagSummaries = (${guidanceTags}) + LET retrievedGuidanceTags = ( + FOR tag IN guidanceTags + FILTER tag._key IN ${tagIds} + ${sortByField} + RETURN MERGE( + { + _id: tag._id, + _key: tag._key, + _rev: tag._rev, + _type: "guidanceTag", + id: tag._key, + tagId: tag._key, + count: TRANSLATE(tag._key, tagSummaries) + }, + TRANSLATE(${language}, tag) + ) + ) + RETURN { + "guidanceTags": retrievedGuidanceTags, + "totalCount": LENGTH(${tagIds}), + } + ` + } catch (err) { + console.error( + `Database error occurred while user: ${userKey} was trying to gather guidance tags in loadGuidanceTagConnectionsByTagId, error: ${err}`, + ) + throw new Error(i18n._(t`Unable to load guidance tag(s). Please try again.`)) + } + + let guidanceTagInfo + try { + guidanceTagInfo = await guidanceTagInfoCursor.next() + } catch (err) { + console.error( + `Cursor error occurred while user: ${userKey} was trying to gather guidance tags in loadGuidanceTagConnectionsByTagId, error: ${err}`, + ) + throw new Error(i18n._(t`Unable to load guidance tag(s). Please try again.`)) + } + + if (guidanceTagInfo.guidanceTags.length === 0) { + return { + guidanceTags: [], + totalCount: 0, + } + } + + return guidanceTagInfo + } diff --git a/api/src/guidance-tag/loaders/load-guidance-tags.js b/api/src/guidance-tag/loaders/load-guidance-tags.js new file mode 100644 index 000000000..3245fa631 --- /dev/null +++ b/api/src/guidance-tag/loaders/load-guidance-tags.js @@ -0,0 +1,40 @@ +import { t } from '@lingui/macro' + +export const loadGuidanceTagByTagId = + ({ query, userKey, i18n, language }) => + async ({ tags = [] }) => { + let cursor + try { + cursor = await query` + WITH guidanceTags + FOR tag IN guidanceTags + FILTER tag._key IN ${tags} + RETURN MERGE( + { + _id: tag._id, + _key: tag._key, + _rev: tag._rev, + _type: "guidanceTag", + id: tag._key, + tagId: tag._key + }, + TRANSLATE(${language}, tag) + ) + ` + } catch (err) { + console.error(`Database error occurred when user: ${userKey} running loadGuidanceTagByTagId: ${err}`) + throw new Error(i18n._(t`Unable to find guidance tag(s). Please try again.`)) + } + + const tagMap = {} + try { + await cursor.forEach((tag) => { + tagMap[tag._key] = tag + }) + } catch (err) { + console.error(`Cursor error occurred when user: ${userKey} running loadGuidanceTagByTagId: ${err}`) + throw new Error(i18n._(t`Unable to find guidance tag(s). Please try again.`)) + } + + return tags.map((tag) => tagMap[tag]) + } diff --git a/api/src/guidance-tag/objects/__tests__/guidance-tag-connection.test.js b/api/src/guidance-tag/objects/__tests__/guidance-tag-connection.test.js index 1af073640..28992c90b 100644 --- a/api/src/guidance-tag/objects/__tests__/guidance-tag-connection.test.js +++ b/api/src/guidance-tag/objects/__tests__/guidance-tag-connection.test.js @@ -1,10 +1,10 @@ -import {GraphQLInt} from 'graphql' -import {guidanceTagConnection} from '../index' +import { GraphQLInt } from 'graphql' +import { guidanceTagConnection } from '../index' describe('given the guidance Tag Connection connection object', () => { describe('testing its field definitions', () => { it('has a totalCount field', () => { - const demoType = guidanceTagConnection.connectionType.getFields() + const demoType = guidanceTagConnection.getFields() expect(demoType).toHaveProperty('totalCount') expect(demoType.totalCount.type).toMatchObject(GraphQLInt) @@ -13,9 +13,9 @@ describe('given the guidance Tag Connection connection object', () => { describe('testing its field resolvers', () => { describe('testing the totalCount resolver', () => { it('returns the resolved value', () => { - const demoType = guidanceTagConnection.connectionType.getFields() + const demoType = guidanceTagConnection.getFields() - expect(demoType.totalCount.resolve({totalCount: 1})).toEqual(1) + expect(demoType.totalCount.resolve({ totalCount: 1 })).toEqual(1) }) }) }) diff --git a/api/src/guidance-tag/objects/guidance-tag-connection.js b/api/src/guidance-tag/objects/guidance-tag-connection.js index 0e0a69360..a6b8c50e2 100644 --- a/api/src/guidance-tag/objects/guidance-tag-connection.js +++ b/api/src/guidance-tag/objects/guidance-tag-connection.js @@ -1,16 +1,18 @@ -import {GraphQLInt} from 'graphql' -import {connectionDefinitions} from 'graphql-relay' +import { GraphQLInt, GraphQLObjectType, GraphQLList } from 'graphql' +import { guidanceTagType } from './guidance-tag' -import {guidanceTagType} from './guidance-tag' - -export const guidanceTagConnection = connectionDefinitions({ - name: 'GuidanceTag', - nodeType: guidanceTagType, - connectionFields: () => ({ +export const guidanceTagConnection = new GraphQLObjectType({ + name: 'GuidanceTagConnection', + fields: () => ({ + guidanceTags: { + type: new GraphQLList(guidanceTagType), + description: '', + resolve: ({ guidanceTags }) => guidanceTags, + }, totalCount: { type: GraphQLInt, description: 'The total amount of guidance tags for a given scan type.', - resolve: ({totalCount}) => totalCount, + resolve: ({ totalCount }) => totalCount, }, }), }) diff --git a/api/src/guidance-tag/objects/guidance-tag.js b/api/src/guidance-tag/objects/guidance-tag.js index 2d11d2255..687c5073d 100644 --- a/api/src/guidance-tag/objects/guidance-tag.js +++ b/api/src/guidance-tag/objects/guidance-tag.js @@ -1,4 +1,4 @@ -import { GraphQLList, GraphQLObjectType, GraphQLString } from 'graphql' +import { GraphQLList, GraphQLObjectType, GraphQLString, GraphQLInt } from 'graphql' import { globalIdField } from 'graphql-relay' import { refLinksType } from './ref-links' @@ -34,6 +34,11 @@ export const guidanceTagType = new GraphQLObjectType({ description: 'Links to technical information for a given tag.', resolve: ({ refLinksTechnical }) => refLinksTechnical, }, + count: { + type: GraphQLInt, + description: 'Number of times the tag has been applied.', + resolve: ({ count }) => count, + }, }), interfaces: [nodeInterface], }) diff --git a/api/src/initialize-loaders.js b/api/src/initialize-loaders.js index dadff3808..1abec1cd2 100644 --- a/api/src/initialize-loaders.js +++ b/api/src/initialize-loaders.js @@ -1,9 +1,3 @@ -import { - loadAffiliationByKey, - loadAffiliationConnectionsByUserId, - loadAffiliationConnectionsByOrgId, -} from './affiliation/loaders' -import { loadAuditLogsByOrgId } from './audit-logs/loaders' import { loadDkimFailConnectionsBySumId, loadDmarcFailConnectionsBySumId, @@ -14,40 +8,10 @@ import { loadSpfFailureConnectionsBySumId, loadStartDateFromPeriod, loadDmarcYearlySumEdge, + loadAllVerifiedRuaDomains, } from './dmarc-summaries/loaders' -import { - loadDomainByKey, - loadDomainByDomain, - loadDomainConnectionsByOrgId, - loadDomainConnectionsByUserId, - loadDkimSelectorsByDomainId, - loadDomainTagsByOrgId, -} from './domain/loaders' -import { - loadAggregateGuidanceTagByTagId, - loadAggregateGuidanceTagConnectionsByTagId, - loadDkimGuidanceTagByTagId, - loadDkimGuidanceTagConnectionsByTagId, - loadDmarcGuidanceTagByTagId, - loadDmarcGuidanceTagConnectionsByTagId, - loadHttpsGuidanceTagByTagId, - loadHttpsGuidanceTagConnectionsByTagId, - loadSpfGuidanceTagByTagId, - loadSpfGuidanceTagConnectionsByTagId, - loadSslGuidanceTagByTagId, - loadSslGuidanceTagConnectionsByTagId, -} from './guidance-tag/loaders' -import { - loadOrgByKey, - loadOrgBySlug, - loadOrgConnectionsByDomainId, - loadOrgConnectionsByUserId, - loadAllOrganizationDomainStatuses, - loadOrganizationDomainStatuses, - loadOrganizationSummariesByPeriod, -} from './organization/loaders' +import { loadOrgByKey, loadOrganizationNamesById } from './organization/loaders' import { loadMyTrackerByUserId, loadUserByUserName, loadUserByKey, loadUserConnectionsByUserId } from './user/loaders' -import { loadWebConnectionsByDomainId, loadWebScansByWebId } from './web-scan/loaders' import { loadVerifiedDomainsById, loadVerifiedDomainByKey, @@ -60,37 +24,9 @@ import { loadVerifiedOrgConnectionsByDomainId, loadVerifiedOrgConnections, } from './verified-organizations/loaders' -import { loadChartSummaryByKey, loadChartSummaryConnectionsByPeriod } from './summaries/loaders' -import { loadDnsConnectionsByDomainId, loadMxRecordDiffByDomainId } from './dns-scan' -export function initializeLoaders({ query, db, userKey, i18n, language, cleanseInput, loginRequiredBool, moment }) { +export function initializeLoaders({ query, userKey, i18n, language, cleanseInput, loginRequiredBool, moment }) { return { - loadChartSummaryByKey: loadChartSummaryByKey({ query, userKey, i18n }), - loadChartSummaryConnectionsByPeriod: loadChartSummaryConnectionsByPeriod({ - query, - userKey, - cleanseInput, - i18n, - }), - loadAggregateGuidanceTagByTagId: loadAggregateGuidanceTagByTagId({ - query, - userKey, - i18n, - language, - }), - loadAggregateGuidanceTagConnectionsByTagId: loadAggregateGuidanceTagConnectionsByTagId({ - query, - userKey, - i18n, - cleanseInput, - language, - }), - loadAuditLogsByOrgId: loadAuditLogsByOrgId({ - query, - userKey, - i18n, - cleanseInput, - }), loadDkimFailConnectionsBySumId: loadDkimFailConnectionsBySumId({ query, userKey, @@ -139,175 +75,8 @@ export function initializeLoaders({ query, db, userKey, i18n, language, cleanseI i18n, }), loadDmarcYearlySumEdge: loadDmarcYearlySumEdge({ query, userKey, i18n }), - loadDomainByDomain: loadDomainByDomain({ query, userKey, i18n }), - loadDomainByKey: loadDomainByKey({ query, userKey, i18n }), - loadDomainConnectionsByOrgId: loadDomainConnectionsByOrgId({ - query, - userKey, - language, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ - query, - userKey, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadDomainTagsByOrgId: loadDomainTagsByOrgId({ query, userKey, i18n }), - loadDnsConnectionsByDomainId: loadDnsConnectionsByDomainId({ - query, - db, - userKey, - cleanseInput, - i18n, - }), - loadMxRecordDiffByDomainId: loadMxRecordDiffByDomainId({ - query, - db, - userKey, - cleanseInput, - i18n, - }), - loadWebConnectionsByDomainId: loadWebConnectionsByDomainId({ - query, - db, - userKey, - cleanseInput, - i18n, - }), - loadWebScansByWebId: loadWebScansByWebId({ - query, - db, - userKey, - cleanseInput, - i18n, - }), - loadDkimGuidanceTagByTagId: loadDkimGuidanceTagByTagId({ - query, - userKey, - i18n, - language, - }), - loadDkimGuidanceTagConnectionsByTagId: loadDkimGuidanceTagConnectionsByTagId({ - query, - userKey, - cleanseInput, - i18n, - language, - }), - loadDmarcGuidanceTagByTagId: loadDmarcGuidanceTagByTagId({ - query, - userKey, - i18n, - language, - }), - loadDmarcGuidanceTagConnectionsByTagId: loadDmarcGuidanceTagConnectionsByTagId({ - query, - userKey, - cleanseInput, - i18n, - language, - }), - loadHttpsGuidanceTagByTagId: loadHttpsGuidanceTagByTagId({ - query, - userKey, - i18n, - language, - }), - loadHttpsGuidanceTagConnectionsByTagId: loadHttpsGuidanceTagConnectionsByTagId({ - query, - userKey, - cleanseInput, - i18n, - language, - }), - loadSpfGuidanceTagByTagId: loadSpfGuidanceTagByTagId({ - query, - userKey, - i18n, - language, - }), - loadSpfGuidanceTagConnectionsByTagId: loadSpfGuidanceTagConnectionsByTagId({ - query, - userKey, - cleanseInput, - i18n, - language, - }), - loadSslGuidanceTagByTagId: loadSslGuidanceTagByTagId({ - query, - userKey, - i18n, - language, - }), - loadSslGuidanceTagConnectionsByTagId: loadSslGuidanceTagConnectionsByTagId({ - query, - userKey, - cleanseInput, - i18n, - language, - }), - loadOrgByKey: loadOrgByKey({ - query, - language, - userKey, - i18n, - }), - loadOrgBySlug: loadOrgBySlug({ - query, - language, - userKey, - i18n, - }), - loadOrgConnectionsByDomainId: loadOrgConnectionsByDomainId({ - query, - language, - userKey, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ - query, - userKey, - cleanseInput, - language, - i18n, - auth: { loginRequiredBool }, - }), - loadOrganizationSummariesByPeriod: loadOrganizationSummariesByPeriod({ - query, - userKey, - cleanseInput, - language, - i18n, - auth: { loginRequiredBool }, - }), - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ - query, - userKey, - cleanseInput, - language, - i18n, - }), - loadOrganizationDomainStatuses: loadOrganizationDomainStatuses({ - query, - userKey, - cleanseInput, - language, - i18n, - auth: { loginRequiredBool }, - }), + loadOrgByKey: loadOrgByKey({ query, language, userKey, i18n }), + loadOrganizationNamesById: loadOrganizationNamesById({ query, userKey, i18n }), loadMyTrackerByUserId: loadMyTrackerByUserId({ query, language, @@ -322,20 +91,6 @@ export function initializeLoaders({ query, db, userKey, i18n, language, cleanseI i18n, }), loadUserByKey: loadUserByKey({ query, userKey, i18n }), - loadAffiliationByKey: loadAffiliationByKey({ query, userKey, i18n }), - loadAffiliationConnectionsByUserId: loadAffiliationConnectionsByUserId({ - query, - language, - userKey, - cleanseInput, - i18n, - }), - loadAffiliationConnectionsByOrgId: loadAffiliationConnectionsByOrgId({ - query, - userKey, - cleanseInput, - i18n, - }), loadVerifiedDomainsById: loadVerifiedDomainsById({ query, i18n }), loadVerifiedDomainByKey: loadVerifiedDomainByKey({ query, i18n }), loadVerifiedDomainConnections: loadVerifiedDomainConnections({ @@ -370,5 +125,10 @@ export function initializeLoaders({ query, db, userKey, i18n, language, cleanseI cleanseInput, i18n, }), + loadAllVerifiedRuaDomains: loadAllVerifiedRuaDomains({ + query, + userKey, + i18n, + }), } } diff --git a/api/src/locale/en/messages.js b/api/src/locale/en/messages.js index b1ad2ca89..1000ee7ac 100644 --- a/api/src/locale/en/messages.js +++ b/api/src/locale/en/messages.js @@ -1 +1 @@ -/*eslint-disable*/module.exports={messages:{"Authentication error. Please sign in.":"Authentication error. Please sign in.","Cannot query affiliations on organization without admin permission or higher.":"Cannot query affiliations on organization without admin permission or higher.","Cannot query audit logs on organization without admin permission or higher.":"Cannot query audit logs on organization without admin permission or higher.","Cannot query dns scan results without permission.":"Cannot query dns scan results without permission.","Cannot query domain selectors without permission.":"Cannot query domain selectors without permission.","Cannot query web scan results without permission.":"Cannot query web scan results without permission.","Email already in use.":"Email already in use.","Error while requesting scan. Please try again.":"Error while requesting scan. Please try again.","If an account with this username is found, a password reset link will be found in your inbox.":"If an account with this username is found, a password reset link will be found in your inbox.","If an account with this username is found, an email verification link will be found in your inbox.":"If an account with this username is found, an email verification link will be found in your inbox.","Incorrect TFA code. Please sign in again.":"Incorrect TFA code. Please sign in again.","Incorrect token value. Please request a new email.":"Incorrect token value. Please request a new email.","Incorrect username or password. Please try again.":"Incorrect username or password. Please try again.","Invalid token, please sign in.":"Invalid token, please sign in.","New passwords do not match.":"New passwords do not match.","No organization with the provided slug could be found.":"No organization with the provided slug could be found.","No verified domain with the provided domain could be found.":"No verified domain with the provided domain could be found.","No verified organization with the provided slug could be found.":"No verified organization with the provided slug could be found.","Organization has already been verified.":"Organization has already been verified.","Organization name already in use, please choose another and try again.":"Organization name already in use, please choose another and try again.","Organization name already in use. Please try again with a different name.":"Organization name already in use. Please try again with a different name.","Ownership check error. Unable to request domain information.":"Ownership check error. Unable to request domain information.","Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.":"Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.","Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported.":"Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported.","Passing both `first` and `last` to paginate the `DKIM` connection is not supported.":"Passing both `first` and `last` to paginate the `DKIM` connection is not supported.","Passing both `first` and `last` to paginate the `DMARC` connection is not supported.":"Passing both `first` and `last` to paginate the `DMARC` connection is not supported.","Passing both `first` and `last` to paginate the `DkimFailureTable` connection is not supported.":"Passing both `first` and `last` to paginate the `DkimFailureTable` connection is not supported.","Passing both `first` and `last` to paginate the `DmarcFailureTable` connection is not supported.":"Passing both `first` and `last` to paginate the `DmarcFailureTable` connection is not supported.","Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported.":"Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported.","Passing both `first` and `last` to paginate the `Domain` connection is not supported.":"Passing both `first` and `last` to paginate the `Domain` connection is not supported.","Passing both `first` and `last` to paginate the `FullPassTable` connection is not supported.":"Passing both `first` and `last` to paginate the `FullPassTable` connection is not supported.","Passing both `first` and `last` to paginate the `GuidanceTag` connection is not supported.":"Passing both `first` and `last` to paginate the `GuidanceTag` connection is not supported.","Passing both `first` and `last` to paginate the `HTTPS` connection is not supported.":"Passing both `first` and `last` to paginate the `HTTPS` connection is not supported.","Passing both `first` and `last` to paginate the `Log` connection is not supported.":"Passing both `first` and `last` to paginate the `Log` connection is not supported.","Passing both `first` and `last` to paginate the `Organization` connection is not supported.":"Passing both `first` and `last` to paginate the `Organization` connection is not supported.","Passing both `first` and `last` to paginate the `SPF` connection is not supported.":"Passing both `first` and `last` to paginate the `SPF` connection is not supported.","Passing both `first` and `last` to paginate the `SSL` connection is not supported.":"Passing both `first` and `last` to paginate the `SSL` connection is not supported.","Passing both `first` and `last` to paginate the `SpfFailureTable` connection is not supported.":"Passing both `first` and `last` to paginate the `SpfFailureTable` connection is not supported.","Passing both `first` and `last` to paginate the `User` connection is not supported.":"Passing both `first` and `last` to paginate the `User` connection is not supported.","Passing both `first` and `last` to paginate the `VerifiedDomain` connection is not supported.":"Passing both `first` and `last` to paginate the `VerifiedDomain` connection is not supported.","Passing both `first` and `last` to paginate the `VerifiedOrganization` connection is not supported.":"Passing both `first` and `last` to paginate the `VerifiedOrganization` connection is not supported.","Password does not meet requirements.":"Password does not meet requirements.","Password was successfully reset.":"Password was successfully reset.","Password was successfully updated.":"Password was successfully updated.","Passwords do not match.":"Passwords do not match.","Permission Denied: Could not retrieve specified organization.":"Permission Denied: Could not retrieve specified organization.","Permission Denied: Multi-factor authentication is required for admin accounts":"Permission Denied: Multi-factor authentication is required for admin accounts","Permission Denied: Please contact org owner to transfer ownership.":"Permission Denied: Please contact org owner to transfer ownership.","Permission Denied: Please contact organization admin for help with archiving domains.":"Permission Denied: Please contact organization admin for help with archiving domains.","Permission Denied: Please contact organization admin for help with removing domain.":"Permission Denied: Please contact organization admin for help with removing domain.","Permission Denied: Please contact organization admin for help with removing domains.":"Permission Denied: Please contact organization admin for help with removing domains.","Permission Denied: Please contact organization admin for help with removing organization.":"Permission Denied: Please contact organization admin for help with removing organization.","Permission Denied: Please contact organization admin for help with removing users.":"Permission Denied: Please contact organization admin for help with removing users.","Permission Denied: Please contact organization admin for help with updating organization.":"Permission Denied: Please contact organization admin for help with updating organization.","Permission Denied: Please contact organization admin for help with updating user roles.":"Permission Denied: Please contact organization admin for help with updating user roles.","Permission Denied: Please contact organization admin for help with user invitations.":"Permission Denied: Please contact organization admin for help with user invitations.","Permission Denied: Please contact organization admin for help with user role changes.":"Permission Denied: Please contact organization admin for help with user role changes.","Permission Denied: Please contact organization user for help with creating domain.":"Permission Denied: Please contact organization user for help with creating domain.","Permission Denied: Please contact organization user for help with creating domains.":"Permission Denied: Please contact organization user for help with creating domains.","Permission Denied: Please contact organization user for help with retrieving this domain.":"Permission Denied: Please contact organization user for help with retrieving this domain.","Permission Denied: Please contact organization user for help with scanning this domain.":"Permission Denied: Please contact organization user for help with scanning this domain.","Permission Denied: Please contact organization user for help with updating this domain.":"Permission Denied: Please contact organization user for help with updating this domain.","Permission Denied: Please contact super admin for help with archiving organization.":"Permission Denied: Please contact super admin for help with archiving organization.","Permission Denied: Please contact super admin for help with removing domain.":"Permission Denied: Please contact super admin for help with removing domain.","Permission Denied: Please contact super admin for help with removing organization.":"Permission Denied: Please contact super admin for help with removing organization.","Permission Denied: Please contact super admin for help with scanning this domain.":"Permission Denied: Please contact super admin for help with scanning this domain.","Permission Denied: Please contact super admin for help with verifying this organization.":"Permission Denied: Please contact super admin for help with verifying this organization.","Permission check error. Unable to request domain information.":"Permission check error. Unable to request domain information.","Permission error, not an admin for this user.":"Permission error, not an admin for this user.","Permission error: Unable to close other user's account.":"Permission error: Unable to close other user's account.","Permissions error. You do not have sufficient permissions to access this data.":"Permissions error. You do not have sufficient permissions to access this data.","Phone number has been successfully removed.":"Phone number has been successfully removed.","Phone number has been successfully set, you will receive a verification text message shortly.":"Phone number has been successfully set, you will receive a verification text message shortly.","Please provide a comment when adding an outside domain.":"Please provide a comment when adding an outside domain.","Profile successfully updated.":"Profile successfully updated.","Requesting `{amount}` records on the `Affiliation` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `Affiliation` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `DkimFailureTable` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `DkimFailureTable` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `DmarcFailureTable` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `DmarcFailureTable` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `DmarcSummaries` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `DmarcSummaries` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `Domain` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `FullPassTable` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `FullPassTable` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `GuidanceTag` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `GuidanceTag` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `Log` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `Log` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `Organization` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `Organization` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `SpfFailureTable` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `SpfFailureTable` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `User` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `User` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `VerifiedDomain` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `VerifiedDomain` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting `{amount}` records on the `VerifiedOrganization` connection exceeds the `{argSet}` limit of 100 records.":["Requesting `",["amount"],"` records on the `VerifiedOrganization` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records.":["Requesting ",["amount"]," records on the `DKIMResults` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records.":["Requesting ",["amount"]," records on the `DKIM` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records.":["Requesting ",["amount"]," records on the `DMARC` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records.":["Requesting ",["amount"]," records on the `HTTPS` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records.":["Requesting ",["amount"]," records on the `SPF` connection exceeds the `",["argSet"],"` limit of 100 records."],"Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records.":["Requesting ",["amount"]," records on the `SSL` connection exceeds the `",["argSet"],"` limit of 100 records."],"Successfully added {domainCount} domain(s) to {0}.":["Successfully added ",["domainCount"]," domain(s) to ",["0"],"."],"Successfully added {domainCount} domains to {0}.":["Successfully added ",["domainCount"]," domains to ",["0"],"."],"Successfully archived organization: {0}.":["Successfully archived organization: ",["0"],"."],"Successfully closed account.":"Successfully closed account.","Successfully dispatched one time scan.":"Successfully dispatched one time scan.","Successfully dispatched subdomain discovery scan.":"Successfully dispatched subdomain discovery scan.","Successfully email verified account, and set TFA send method to email.":"Successfully email verified account, and set TFA send method to email.","Successfully invited user to organization, and sent notification email.":"Successfully invited user to organization, and sent notification email.","Successfully left organization: {0}":["Successfully left organization: ",["0"]],"Successfully removed domain: {0} from favourites.":["Successfully removed domain: ",["0"]," from favourites."],"Successfully removed domain: {0} from {1}.":["Successfully removed domain: ",["0"]," from ",["1"],"."],"Successfully removed organization: {0}.":["Successfully removed organization: ",["0"],"."],"Successfully removed user from organization.":"Successfully removed user from organization.","Successfully removed {domainCount} domain(s) from {0}.":["Successfully removed ",["domainCount"]," domain(s) from ",["0"],"."],"Successfully removed {domainCount} domains from {0}.":["Successfully removed ",["domainCount"]," domains from ",["0"],"."],"Successfully requested invite to organization, and sent notification email.":"Successfully requested invite to organization, and sent notification email.","Successfully sent invitation to service, and organization email.":"Successfully sent invitation to service, and organization email.","Successfully signed out.":"Successfully signed out.","Successfully transferred org: {0} ownership to user: {1}":["Successfully transferred org: ",["0"]," ownership to user: ",["1"]],"Successfully verified organization: {0}.":["Successfully verified organization: ",["0"],"."],"Successfully verified phone number, and set TFA send method to text.":"Successfully verified phone number, and set TFA send method to text.","Token value incorrect, please sign in again.":"Token value incorrect, please sign in again.","Too many failed login attempts, please reset your password, and try again.":"Too many failed login attempts, please reset your password, and try again.","Two factor code is incorrect. Please try again.":"Two factor code is incorrect. Please try again.","Two factor code length is incorrect. Please try again.":"Two factor code length is incorrect. Please try again.","Unable leave organization. Please try again.":"Unable leave organization. Please try again.","Unable to add domains in unknown organization.":"Unable to add domains in unknown organization.","Unable to archive organization. Please try again.":"Unable to archive organization. Please try again.","Unable to archive unknown organization.":"Unable to archive unknown organization.","Unable to authenticate. Please try again.":"Unable to authenticate. Please try again.","Unable to check permission. Please try again.":"Unable to check permission. Please try again.","Unable to close account of an undefined user.":"Unable to close account of an undefined user.","Unable to close account. Please try again.":"Unable to close account. Please try again.","Unable to create domain in unknown organization.":"Unable to create domain in unknown organization.","Unable to create domain, organization has already claimed it.":"Unable to create domain, organization has already claimed it.","Unable to create domain. Please try again.":"Unable to create domain. Please try again.","Unable to create domains. Please try again.":"Unable to create domains. Please try again.","Unable to create organization. Please try again.":"Unable to create organization. Please try again.","Unable to discover domains for unknown organization.":"Unable to discover domains for unknown organization.","Unable to dispatch one time scan. Please try again.":"Unable to dispatch one time scan. Please try again.","Unable to export organization. Please try again.":"Unable to export organization. Please try again.","Unable to favourite domain, user has already favourited it.":"Unable to favourite domain, user has already favourited it.","Unable to favourite domain. Please try again.":"Unable to favourite domain. Please try again.","Unable to favourite unknown domain.":"Unable to favourite unknown domain.","Unable to find Aggregate guidance tag(s). Please try again.":"Unable to find Aggregate guidance tag(s). Please try again.","Unable to find DKIM guidance tag(s). Please try again.":"Unable to find DKIM guidance tag(s). Please try again.","Unable to find DKIM result(s). Please try again.":"Unable to find DKIM result(s). Please try again.","Unable to find DKIM scan(s). Please try again.":"Unable to find DKIM scan(s). Please try again.","Unable to find DMARC guidance tag(s). Please try again.":"Unable to find DMARC guidance tag(s). Please try again.","Unable to find DMARC scan(s). Please try again.":"Unable to find DMARC scan(s). Please try again.","Unable to find DMARC summary data. Please try again.":"Unable to find DMARC summary data. Please try again.","Unable to find DNS scan(s). Please try again.":"Unable to find DNS scan(s). Please try again.","Unable to find HTTPS guidance tag(s). Please try again.":"Unable to find HTTPS guidance tag(s). Please try again.","Unable to find HTTPS scan(s). Please try again.":"Unable to find HTTPS scan(s). Please try again.","Unable to find SPF guidance tag(s). Please try again.":"Unable to find SPF guidance tag(s). Please try again.","Unable to find SPF scan(s). Please try again.":"Unable to find SPF scan(s). Please try again.","Unable to find SSL guidance tag(s). Please try again.":"Unable to find SSL guidance tag(s). Please try again.","Unable to find SSL scan(s). Please try again.":"Unable to find SSL scan(s). Please try again.","Unable to find the requested domain.":"Unable to find the requested domain.","Unable to find user affiliation(s). Please try again.":"Unable to find user affiliation(s). Please try again.","Unable to find verified organization(s). Please try again.":"Unable to find verified organization(s). Please try again.","Unable to invite user to organization. Please try again.":"Unable to invite user to organization. Please try again.","Unable to invite user to organization. User is already affiliated with organization.":"Unable to invite user to organization. User is already affiliated with organization.","Unable to invite user to unknown organization.":"Unable to invite user to unknown organization.","Unable to invite user. Please try again.":"Unable to invite user. Please try again.","Unable to invite yourself to an org.":"Unable to invite yourself to an org.","Unable to leave organization. Please try again.":"Unable to leave organization. Please try again.","Unable to leave undefined organization.":"Unable to leave undefined organization.","Unable to load Aggregate guidance tag(s). Please try again.":"Unable to load Aggregate guidance tag(s). Please try again.","Unable to load DKIM failure data. Please try again.":"Unable to load DKIM failure data. Please try again.","Unable to load DKIM guidance tag(s). Please try again.":"Unable to load DKIM guidance tag(s). Please try again.","Unable to load DKIM result(s). Please try again.":"Unable to load DKIM result(s). Please try again.","Unable to load DKIM scan(s). Please try again.":"Unable to load DKIM scan(s). Please try again.","Unable to load DKIM summary. Please try again.":"Unable to load DKIM summary. Please try again.","Unable to load DMARC failure data. Please try again.":"Unable to load DMARC failure data. Please try again.","Unable to load DMARC guidance tag(s). Please try again.":"Unable to load DMARC guidance tag(s). Please try again.","Unable to load DMARC phase summary. Please try again.":"Unable to load DMARC phase summary. Please try again.","Unable to load DMARC scan(s). Please try again.":"Unable to load DMARC scan(s). Please try again.","Unable to load DMARC summary data. Please try again.":"Unable to load DMARC summary data. Please try again.","Unable to load DMARC summary. Please try again.":"Unable to load DMARC summary. Please try again.","Unable to load DNS scan(s). Please try again.":"Unable to load DNS scan(s). Please try again.","Unable to load HTTPS guidance tag(s). Please try again.":"Unable to load HTTPS guidance tag(s). Please try again.","Unable to load HTTPS scan(s). Please try again.":"Unable to load HTTPS scan(s). Please try again.","Unable to load HTTPS summary. Please try again.":"Unable to load HTTPS summary. Please try again.","Unable to load SPF failure data. Please try again.":"Unable to load SPF failure data. Please try again.","Unable to load SPF guidance tag(s). Please try again.":"Unable to load SPF guidance tag(s). Please try again.","Unable to load SPF scan(s). Please try again.":"Unable to load SPF scan(s). Please try again.","Unable to load SPF summary. Please try again.":"Unable to load SPF summary. Please try again.","Unable to load SSL guidance tag(s). Please try again.":"Unable to load SSL guidance tag(s). Please try again.","Unable to load SSL scan(s). Please try again.":"Unable to load SSL scan(s). Please try again.","Unable to load SSL summary. Please try again.":"Unable to load SSL summary. Please try again.","Unable to load affiliation information. Please try again.":"Unable to load affiliation information. Please try again.","Unable to load affiliation(s). Please try again.":"Unable to load affiliation(s). Please try again.","Unable to load all organization domain statuses. Please try again.":"Unable to load all organization domain statuses. Please try again.","Unable to load domain(s). Please try again.":"Unable to load domain(s). Please try again.","Unable to load domain. Please try again.":"Unable to load domain. Please try again.","Unable to load full pass data. Please try again.":"Unable to load full pass data. Please try again.","Unable to load log(s). Please try again.":"Unable to load log(s). Please try again.","Unable to load log. Please try again.":"Unable to load log. Please try again.","Unable to load mail summary. Please try again.":"Unable to load mail summary. Please try again.","Unable to load organization domain statuses. Please try again.":"Unable to load organization domain statuses. Please try again.","Unable to load organization(s). Please try again.":"Unable to load organization(s). Please try again.","Unable to load owner information. Please try again.":"Unable to load owner information. Please try again.","Unable to load summary. Please try again.":"Unable to load summary. Please try again.","Unable to load tags(s). Please try again.":"Unable to load tags(s). Please try again.","Unable to load user(s). Please try again.":"Unable to load user(s). Please try again.","Unable to load verified domain(s). Please try again.":"Unable to load verified domain(s). Please try again.","Unable to load verified organization(s). Please try again.":"Unable to load verified organization(s). Please try again.","Unable to load web connections summary. Please try again.":"Unable to load web connections summary. Please try again.","Unable to load web scan(s). Please try again.":"Unable to load web scan(s). Please try again.","Unable to load web summary. Please try again.":"Unable to load web summary. Please try again.","Unable to query affiliation(s). Please try again.":"Unable to query affiliation(s). Please try again.","Unable to query domain(s). Please try again.":"Unable to query domain(s). Please try again.","Unable to query log(s). Please try again.":"Unable to query log(s). Please try again.","Unable to query user(s). Please try again.":"Unable to query user(s). Please try again.","Unable to refresh tokens, please sign in.":"Unable to refresh tokens, please sign in.","Unable to remove a user that already does not belong to this organization.":"Unable to remove a user that already does not belong to this organization.","Unable to remove domain from unknown organization.":"Unable to remove domain from unknown organization.","Unable to remove domain. Domain is not part of organization.":"Unable to remove domain. Domain is not part of organization.","Unable to remove domain. Please try again.":"Unable to remove domain. Please try again.","Unable to remove domains from unknown organization.":"Unable to remove domains from unknown organization.","Unable to remove organization. Please try again.":"Unable to remove organization. Please try again.","Unable to remove phone number. Please try again.":"Unable to remove phone number. Please try again.","Unable to remove unknown domain.":"Unable to remove unknown domain.","Unable to remove unknown organization.":"Unable to remove unknown organization.","Unable to remove unknown user from organization.":"Unable to remove unknown user from organization.","Unable to remove user from organization.":"Unable to remove user from organization.","Unable to remove user from this organization. Please try again.":"Unable to remove user from this organization. Please try again.","Unable to remove user from unknown organization.":"Unable to remove user from unknown organization.","Unable to request a one time scan on a domain that already has a pending scan.":"Unable to request a one time scan on a domain that already has a pending scan.","Unable to request a one time scan on an unknown domain.":"Unable to request a one time scan on an unknown domain.","Unable to request a one time scan. Please try again.":"Unable to request a one time scan. Please try again.","Unable to request a subdomain discovery scan on an invalid domain.":"Unable to request a subdomain discovery scan on an invalid domain.","Unable to request a subdomain discovery scan on an unknown domain.":"Unable to request a subdomain discovery scan on an unknown domain.","Unable to request invite to organization with which you are already affiliated.":"Unable to request invite to organization with which you are already affiliated.","Unable to request invite to organization with which you have already requested to join.":"Unable to request invite to organization with which you have already requested to join.","Unable to request invite to unknown organization.":"Unable to request invite to unknown organization.","Unable to request invite. Please try again.":"Unable to request invite. Please try again.","Unable to reset password. Please request a new email.":"Unable to reset password. Please request a new email.","Unable to reset password. Please try again.":"Unable to reset password. Please try again.","Unable to retrieve DMARC report information for: {domain}":["Unable to retrieve DMARC report information for: ",["domain"]],"Unable to select DMARC report(s) for this period and year.":"Unable to select DMARC report(s) for this period and year.","Unable to send authentication email. Please try again.":"Unable to send authentication email. Please try again.","Unable to send authentication text message. Please try again.":"Unable to send authentication text message. Please try again.","Unable to send org invite email. Please try again.":"Unable to send org invite email. Please try again.","Unable to send org invite request email. Please try again.":"Unable to send org invite request email. Please try again.","Unable to send password reset email. Please try again.":"Unable to send password reset email. Please try again.","Unable to send two factor authentication message. Please try again.":"Unable to send two factor authentication message. Please try again.","Unable to send verification email. Please try again.":"Unable to send verification email. Please try again.","Unable to set phone number, please try again.":"Unable to set phone number, please try again.","Unable to sign in, please try again.":"Unable to sign in, please try again.","Unable to sign up, please contact org admin for a new invite.":"Unable to sign up, please contact org admin for a new invite.","Unable to sign up. Please try again.":"Unable to sign up. Please try again.","Unable to transfer organization ownership. Please try again.":"Unable to transfer organization ownership. Please try again.","Unable to transfer ownership of a verified organization.":"Unable to transfer ownership of a verified organization.","Unable to transfer ownership of an org to an undefined user.":"Unable to transfer ownership of an org to an undefined user.","Unable to transfer ownership of undefined organization.":"Unable to transfer ownership of undefined organization.","Unable to transfer ownership to a user outside the org. Please invite the user and try again.":"Unable to transfer ownership to a user outside the org. Please invite the user and try again.","Unable to two factor authenticate. Please try again.":"Unable to two factor authenticate. Please try again.","Unable to unfavourite domain, domain is not favourited.":"Unable to unfavourite domain, domain is not favourited.","Unable to unfavourite domain. Please try again.":"Unable to unfavourite domain. Please try again.","Unable to unfavourite unknown domain.":"Unable to unfavourite unknown domain.","Unable to update domain edge. Please try again.":"Unable to update domain edge. Please try again.","Unable to update domain in an unknown org.":"Unable to update domain in an unknown org.","Unable to update domain that does not belong to the given organization.":"Unable to update domain that does not belong to the given organization.","Unable to update domain. Please try again.":"Unable to update domain. Please try again.","Unable to update organization. Please try again.":"Unable to update organization. Please try again.","Unable to update password, current password does not match. Please try again.":"Unable to update password, current password does not match. Please try again.","Unable to update password, new passwords do not match. Please try again.":"Unable to update password, new passwords do not match. Please try again.","Unable to update password, passwords do not match requirements. Please try again.":"Unable to update password, passwords do not match requirements. Please try again.","Unable to update password. Please try again.":"Unable to update password. Please try again.","Unable to update profile. Please try again.":"Unable to update profile. Please try again.","Unable to update role: organization unknown.":"Unable to update role: organization unknown.","Unable to update role: user does not belong to organization.":"Unable to update role: user does not belong to organization.","Unable to update role: user unknown.":"Unable to update role: user unknown.","Unable to update unknown domain.":"Unable to update unknown domain.","Unable to update unknown organization.":"Unable to update unknown organization.","Unable to update user's role. Please try again.":"Unable to update user's role. Please try again.","Unable to update your own role.":"Unable to update your own role.","Unable to verify account. Please request a new email.":"Unable to verify account. Please request a new email.","Unable to verify account. Please try again.":"Unable to verify account. Please try again.","Unable to verify if user is a super admin, please try again.":"Unable to verify if user is a super admin, please try again.","Unable to verify if user is an admin, please try again.":"Unable to verify if user is an admin, please try again.","Unable to verify organization. Please try again.":"Unable to verify organization. Please try again.","Unable to verify unknown organization.":"Unable to verify unknown organization.","User could not be queried.":"User could not be queried.","User role was updated successfully.":"User role was updated successfully.","Username not available, please try another.":"Username not available, please try another.","Verification error. Please activate multi-factor authentication to access content.":"Verification error. Please activate multi-factor authentication to access content.","Verification error. Please verify your account via email to access content.":"Verification error. Please verify your account via email to access content.","You must provide a `first` or `last` value to properly paginate the `Affiliation` connection.":"You must provide a `first` or `last` value to properly paginate the `Affiliation` connection.","You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection.":"You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection.","You must provide a `first` or `last` value to properly paginate the `DKIM` connection.":"You must provide a `first` or `last` value to properly paginate the `DKIM` connection.","You must provide a `first` or `last` value to properly paginate the `DMARC` connection.":"You must provide a `first` or `last` value to properly paginate the `DMARC` connection.","You must provide a `first` or `last` value to properly paginate the `DkimFailureTable` connection.":"You must provide a `first` or `last` value to properly paginate the `DkimFailureTable` connection.","You must provide a `first` or `last` value to properly paginate the `DmarcFailureTable` connection.":"You must provide a `first` or `last` value to properly paginate the `DmarcFailureTable` connection.","You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection.":"You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection.","You must provide a `first` or `last` value to properly paginate the `Domain` connection.":"You must provide a `first` or `last` value to properly paginate the `Domain` connection.","You must provide a `first` or `last` value to properly paginate the `FullPassTable` connection.":"You must provide a `first` or `last` value to properly paginate the `FullPassTable` connection.","You must provide a `first` or `last` value to properly paginate the `GuidanceTag` connection.":"You must provide a `first` or `last` value to properly paginate the `GuidanceTag` connection.","You must provide a `first` or `last` value to properly paginate the `HTTPS` connection.":"You must provide a `first` or `last` value to properly paginate the `HTTPS` connection.","You must provide a `first` or `last` value to properly paginate the `Log` connection.":"You must provide a `first` or `last` value to properly paginate the `Log` connection.","You must provide a `first` or `last` value to properly paginate the `Organization` connection.":"You must provide a `first` or `last` value to properly paginate the `Organization` connection.","You must provide a `first` or `last` value to properly paginate the `SPF` connection.":"You must provide a `first` or `last` value to properly paginate the `SPF` connection.","You must provide a `first` or `last` value to properly paginate the `SSL` connection.":"You must provide a `first` or `last` value to properly paginate the `SSL` connection.","You must provide a `first` or `last` value to properly paginate the `SpfFailureTable` connection.":"You must provide a `first` or `last` value to properly paginate the `SpfFailureTable` connection.","You must provide a `first` or `last` value to properly paginate the `User` connection.":"You must provide a `first` or `last` value to properly paginate the `User` connection.","You must provide a `first` or `last` value to properly paginate the `VerifiedDomain` connection.":"You must provide a `first` or `last` value to properly paginate the `VerifiedDomain` connection.","You must provide a `first` or `last` value to properly paginate the `VerifiedOrganization` connection.":"You must provide a `first` or `last` value to properly paginate the `VerifiedOrganization` connection.","You must provide a `limit` value in the range of 1-100 to properly paginate the `DNS` connection.":"You must provide a `limit` value in the range of 1-100 to properly paginate the `DNS` connection.","You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection.":"You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection.","You must provide a `limit` value in the range of 1-100 to properly paginate the `web` connection.":"You must provide a `limit` value in the range of 1-100 to properly paginate the `web` connection.","You must provide a `limit` value to properly paginate the `DNS` connection.":"You must provide a `limit` value to properly paginate the `DNS` connection.","You must provide a `limit` value to properly paginate the `MXRecord` connection.":"You must provide a `limit` value to properly paginate the `MXRecord` connection.","You must provide a `limit` value to properly paginate the `web` connection.":"You must provide a `limit` value to properly paginate the `web` connection.","You must provide a `period` value to access the `DmarcSummaries` connection.":"You must provide a `period` value to access the `DmarcSummaries` connection.","You must provide a `year` value to access the `DmarcSummaries` connection.":"You must provide a `year` value to access the `DmarcSummaries` connection.","You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `DNS` connection.":"You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `DNS` connection.","You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection.":"You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection.","You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `web` connection.":"You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `web` connection.","`{argSet}` must be of type `number` not `{typeSet}`.":["`",["argSet"],"` must be of type `number` not `",["typeSet"],"`."],"`{argSet}` on the `Affiliation` connection cannot be less than zero.":["`",["argSet"],"` on the `Affiliation` connection cannot be less than zero."],"`{argSet}` on the `DKIMResults` connection cannot be less than zero.":["`",["argSet"],"` on the `DKIMResults` connection cannot be less than zero."],"`{argSet}` on the `DKIM` connection cannot be less than zero.":["`",["argSet"],"` on the `DKIM` connection cannot be less than zero."],"`{argSet}` on the `DMARC` connection cannot be less than zero.":["`",["argSet"],"` on the `DMARC` connection cannot be less than zero."],"`{argSet}` on the `DkimFailureTable` connection cannot be less than zero.":["`",["argSet"],"` on the `DkimFailureTable` connection cannot be less than zero."],"`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero.":["`",["argSet"],"` on the `DmarcFailureTable` connection cannot be less than zero."],"`{argSet}` on the `DmarcSummaries` connection cannot be less than zero.":["`",["argSet"],"` on the `DmarcSummaries` connection cannot be less than zero."],"`{argSet}` on the `Domain` connection cannot be less than zero.":["`",["argSet"],"` on the `Domain` connection cannot be less than zero."],"`{argSet}` on the `FullPassTable` connection cannot be less than zero.":["`",["argSet"],"` on the `FullPassTable` connection cannot be less than zero."],"`{argSet}` on the `GuidanceTag` connection cannot be less than zero.":["`",["argSet"],"` on the `GuidanceTag` connection cannot be less than zero."],"`{argSet}` on the `HTTPS` connection cannot be less than zero.":["`",["argSet"],"` on the `HTTPS` connection cannot be less than zero."],"`{argSet}` on the `Log` connection cannot be less than zero.":["`",["argSet"],"` on the `Log` connection cannot be less than zero."],"`{argSet}` on the `Organization` connection cannot be less than zero.":["`",["argSet"],"` on the `Organization` connection cannot be less than zero."],"`{argSet}` on the `SPF` connection cannot be less than zero.":["`",["argSet"],"` on the `SPF` connection cannot be less than zero."],"`{argSet}` on the `SSL` connection cannot be less than zero.":["`",["argSet"],"` on the `SSL` connection cannot be less than zero."],"`{argSet}` on the `SpfFailureTable` connection cannot be less than zero.":["`",["argSet"],"` on the `SpfFailureTable` connection cannot be less than zero."],"`{argSet}` on the `User` connection cannot be less than zero.":["`",["argSet"],"` on the `User` connection cannot be less than zero."],"`{argSet}` on the `VerifiedDomain` connection cannot be less than zero.":["`",["argSet"],"` on the `VerifiedDomain` connection cannot be less than zero."],"`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero.":["`",["argSet"],"` on the `VerifiedOrganization` connection cannot be less than zero."]}}; \ No newline at end of file +/*eslint-disable*/module.exports={messages:JSON.parse("{\"Eoi1qW\":[\"`\",[\"argSet\"],\"` must be of type `number` not `\",[\"typeSet\"],\"`.\"],\"uPlrHl\":[\"`\",[\"argSet\"],\"` on the `Affiliation` connection cannot be less than zero.\"],\"EtaVdR\":[\"`\",[\"argSet\"],\"` on the `DKIM` connection cannot be less than zero.\"],\"2npn9d\":[\"`\",[\"argSet\"],\"` on the `DkimFailureTable` connection cannot be less than zero.\"],\"mpaL2O\":[\"`\",[\"argSet\"],\"` on the `DKIMResults` connection cannot be less than zero.\"],\"2vz4+a\":[\"`\",[\"argSet\"],\"` on the `DMARC` connection cannot be less than zero.\"],\"ujwWhu\":[\"`\",[\"argSet\"],\"` on the `DmarcFailureTable` connection cannot be less than zero.\"],\"HbNnNq\":[\"`\",[\"argSet\"],\"` on the `DmarcSummaries` connection cannot be less than zero.\"],\"eSKAjw\":[\"`\",[\"argSet\"],\"` on the `Domain` connection cannot be less than zero.\"],\"R2ityD\":[\"`\",[\"argSet\"],\"` on the `FullPassTable` connection cannot be less than zero.\"],\"dqZo9M\":[\"`\",[\"argSet\"],\"` on the `GuidanceTag` connection cannot be less than zero.\"],\"YXZolK\":[\"`\",[\"argSet\"],\"` on the `HTTPS` connection cannot be less than zero.\"],\"UACahy\":[\"`\",[\"argSet\"],\"` on the `Log` connection cannot be less than zero.\"],\"nkUPC9\":[\"`\",[\"argSet\"],\"` on the `Organization` connection cannot be less than zero.\"],\"Jkk2ml\":[\"`\",[\"argSet\"],\"` on the `SPF` connection cannot be less than zero.\"],\"Rfl7ba\":[\"`\",[\"argSet\"],\"` on the `SpfFailureTable` connection cannot be less than zero.\"],\"w6BMWR\":[\"`\",[\"argSet\"],\"` on the `SSL` connection cannot be less than zero.\"],\"kk1tk/\":[\"`\",[\"argSet\"],\"` on the `User` connection cannot be less than zero.\"],\"6ZERsG\":[\"`\",[\"argSet\"],\"` on the `VerifiedDomain` connection cannot be less than zero.\"],\"plfOI4\":[\"`\",[\"argSet\"],\"` on the `VerifiedOrganization` connection cannot be less than zero.\"],\"w3MeQ2\":\"Authentication error. Please sign in.\",\"9QA26B\":\"Cannot query additional findings without permission.\",\"OEzMB0\":\"Cannot query affiliations on organization without admin permission or higher.\",\"2K0+0j\":\"Cannot query audit logs on organization without admin permission or higher.\",\"Z2VjXv\":\"Cannot query dns scan results without permission.\",\"SsrKFI\":\"Cannot query domain selectors without permission.\",\"/fzcPY\":\"Cannot query web scan results without permission.\",\"uWrncA\":\"CVE is already ignored for this domain.\",\"messFf\":\"CVE is not ignored for this domain.\",\"K9BF0u\":\"Email already in use.\",\"kr40ez\":\"Error while requesting scan. Please try again.\",\"xty1cI\":\"If an account with this username is found, a password reset link will be found in your inbox.\",\"n3yyu8\":\"If an account with this username is found, an email verification link will be found in your inbox.\",\"YPojZ6\":\"Incorrect TFA code. Please sign in again.\",\"q/Mmq8\":\"Incorrect token value. Please request a new email.\",\"MXwSlX\":\"Incorrect username or password. Please try again.\",\"1YEhLV\":\"Invalid token, please sign in.\",\"ydzoW2\":\"Message dismissed successfully\",\"6Ylhna\":\"New passwords do not match.\",\"BwHoFc\":\"No organization with the provided slug could be found.\",\"MnkZ16\":\"No verified domain with the provided domain could be found.\",\"L9hADR\":\"No verified organization with the provided slug could be found.\",\"uYSCJj\":\"Organization has already been verified.\",\"p20l3d\":\"Organization name already in use, please choose another and try again.\",\"SVGjT0\":\"Organization name already in use. Please try again with a different name.\",\"dndFWA\":\"Ownership check error. Unable to request domain information.\",\"Drhmz3\":\"Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.\",\"85YUUC\":\"Passing both `first` and `last` to paginate the `DKIM` connection is not supported.\",\"ELjtwQ\":\"Passing both `first` and `last` to paginate the `DkimFailureTable` connection is not supported.\",\"fh3YJO\":\"Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported.\",\"x4tq+e\":\"Passing both `first` and `last` to paginate the `DMARC` connection is not supported.\",\"594X4Y\":\"Passing both `first` and `last` to paginate the `DmarcFailureTable` connection is not supported.\",\"X0oiNk\":\"Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported.\",\"k0nHGG\":\"Passing both `first` and `last` to paginate the `Domain` connection is not supported.\",\"qvXcAG\":\"Passing both `first` and `last` to paginate the `FullPassTable` connection is not supported.\",\"iC7wND\":\"Passing both `first` and `last` to paginate the `GuidanceTag` connection is not supported.\",\"oiwNsK\":\"Passing both `first` and `last` to paginate the `HTTPS` connection is not supported.\",\"UQKJcw\":\"Passing both `first` and `last` to paginate the `Log` connection is not supported.\",\"9QRMXj\":\"Passing both `first` and `last` to paginate the `Organization` connection is not supported.\",\"y0439x\":\"Passing both `first` and `last` to paginate the `SPF` connection is not supported.\",\"kp3U0m\":\"Passing both `first` and `last` to paginate the `SpfFailureTable` connection is not supported.\",\"o4LzIu\":\"Passing both `first` and `last` to paginate the `SSL` connection is not supported.\",\"eMCdJf\":\"Passing both `first` and `last` to paginate the `User` connection is not supported.\",\"9lvjn+\":\"Passing both `first` and `last` to paginate the `VerifiedDomain` connection is not supported.\",\"diyob+\":\"Passing both `first` and `last` to paginate the `VerifiedOrganization` connection is not supported.\",\"t0Yhq7\":\"Password does not meet requirements.\",\"JweT52\":\"Password was successfully reset.\",\"U7wcn8\":\"Password was successfully updated.\",\"fDGOiR\":\"Passwords do not match.\",\"VJ90eS\":\"Permission check error. Unable to request domain information.\",\"smjfcW\":\"Permission Denied: Could not retrieve specified organization.\",\"6b0Uzo\":\"Permission Denied: Multi-factor authentication is required for admin accounts\",\"d7fLWy\":\"Permission Denied: Please contact org owner to transfer ownership.\",\"xBt+I5\":\"Permission Denied: Please contact organization admin for help with archiving domains.\",\"qqp3YV\":\"Permission Denied: Please contact organization admin for help with removing domain.\",\"9ty0BN\":\"Permission Denied: Please contact organization admin for help with removing domains.\",\"XLwFgy\":\"Permission Denied: Please contact organization admin for help with removing organization.\",\"AJ7T8X\":\"Permission Denied: Please contact organization admin for help with removing users.\",\"9MP0sV\":\"Permission Denied: Please contact organization admin for help with updating organization.\",\"9/MaeX\":\"Permission Denied: Please contact organization admin for help with updating user roles.\",\"gJAEM/\":\"Permission Denied: Please contact organization admin for help with user invitations.\",\"b+aSK2\":\"Permission Denied: Please contact organization admin for help with user role changes.\",\"MkIId8\":\"Permission Denied: Please contact organization user for help with creating domain.\",\"zCsJJ+\":\"Permission Denied: Please contact organization user for help with creating domains.\",\"zXXF5h\":\"Permission Denied: Please contact organization user for help with retrieving this domain.\",\"wI7ogy\":\"Permission Denied: Please contact organization user for help with scanning this domain.\",\"MU2+6C\":\"Permission Denied: Please contact organization user for help with updating this domain.\",\"WO5Xev\":\"Permission Denied: Please contact super admin for help with archiving organization.\",\"uf+M40\":\"Permission Denied: Please contact super admin for help with removing domain.\",\"Sf3vFY\":\"Permission Denied: Please contact super admin for help with removing organization.\",\"kJ+fSd\":\"Permission Denied: Please contact super admin for help with scanning this domain.\",\"c11gmL\":\"Permission Denied: Please contact super admin for help with verifying this organization.\",\"Kwq7PZ\":\"Permission error, not an admin for this user.\",\"ht7RB9\":\"Permission error: Unable to close other user's account.\",\"S3055l\":\"Permissions error. You do not have sufficient permissions to access this data.\",\"qj6rps\":\"Phone number has been successfully removed.\",\"uEAFen\":\"Phone number has been successfully set, you will receive a verification text message shortly.\",\"LbgZEx\":\"Please provide a comment when adding an outside domain.\",\"yu5LUi\":\"Profile successfully updated.\",\"22PG3h\":[\"Requesting \",[\"amount\"],\" records on the `DKIM` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"lEZ2x7\":[\"Requesting \",[\"amount\"],\" records on the `DKIMResults` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"3kc6YF\":[\"Requesting \",[\"amount\"],\" records on the `DMARC` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"wxwMqQ\":[\"Requesting \",[\"amount\"],\" records on the `HTTPS` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"qAmoqx\":[\"Requesting \",[\"amount\"],\" records on the `SPF` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"JD82Qj\":[\"Requesting \",[\"amount\"],\" records on the `SSL` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"ocE6aN\":[\"Requesting `\",[\"amount\"],\"` records on the `Affiliation` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"VF35kt\":[\"Requesting `\",[\"amount\"],\"` records on the `DkimFailureTable` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"fPydkO\":[\"Requesting `\",[\"amount\"],\"` records on the `DmarcFailureTable` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"zOGaYb\":[\"Requesting `\",[\"amount\"],\"` records on the `DmarcSummaries` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"oSdPzK\":[\"Requesting `\",[\"amount\"],\"` records on the `Domain` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"sfbRxl\":[\"Requesting `\",[\"amount\"],\"` records on the `FullPassTable` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"9ECkIl\":[\"Requesting `\",[\"amount\"],\"` records on the `GuidanceTag` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"u4JPdU\":[\"Requesting `\",[\"amount\"],\"` records on the `Log` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"UuKFug\":[\"Requesting `\",[\"amount\"],\"` records on the `Organization` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"ytIe4o\":[\"Requesting `\",[\"amount\"],\"` records on the `SpfFailureTable` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"5vNdM3\":[\"Requesting `\",[\"amount\"],\"` records on the `User` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"SfCdEm\":[\"Requesting `\",[\"amount\"],\"` records on the `VerifiedDomain` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"4Vvv6j\":[\"Requesting `\",[\"amount\"],\"` records on the `VerifiedOrganization` connection exceeds the `\",[\"argSet\"],\"` limit of 100 records.\"],\"FPrtxO\":[\"Successfully added \",[\"domainCount\"],\" domain(s) to \",[\"0\"],\".\"],\"aNKZ2s\":[\"Successfully added \",[\"domainCount\"],\" domains to \",[\"0\"],\".\"],\"6kD2ow\":[\"Successfully archived organization: \",[\"0\"],\".\"],\"du9Xou\":\"Successfully closed account.\",\"/XMSy3\":\"Successfully dispatched one time scan.\",\"EknPJ9\":\"Successfully dispatched subdomain discovery scan.\",\"WnhHoM\":\"Successfully email verified account, and set TFA send method to email.\",\"ZekZu1\":\"Successfully email verified account.\",\"v/sjus\":\"Successfully invited user to organization, and sent notification email.\",\"NOS/E2\":[\"Successfully left organization: \",[\"0\"]],\"Mb7h0p\":[\"Successfully removed \",[\"domainCount\"],\" domain(s) from \",[\"0\"],\".\"],\"whT9qL\":[\"Successfully removed \",[\"domainCount\"],\" domains from \",[\"0\"],\".\"],\"KJoIYL\":[\"Successfully removed domain: \",[\"0\"],\" from \",[\"1\"],\".\"],\"B3bhOU\":[\"Successfully removed domain: \",[\"0\"],\" from favourites.\"],\"6UbMHy\":[\"Successfully removed organization: \",[\"0\"],\".\"],\"m3IqsS\":\"Successfully removed user from organization.\",\"whspKB\":\"Successfully requested invite to organization, and sent notification email.\",\"hsTfau\":\"Successfully sent invitation to service, and organization email.\",\"92cunG\":\"Successfully signed out.\",\"UhOnUX\":[\"Successfully transferred org: \",[\"0\"],\" ownership to user: \",[\"1\"]],\"QLcXYT\":[\"Successfully verified organization: \",[\"0\"],\".\"],\"0UHOyT\":\"Successfully verified phone number, and set TFA send method to text.\",\"G+Imw/\":\"Token value incorrect, please sign in again.\",\"bTMB63\":\"Too many failed login attempts, please reset your password, and try again.\",\"KPk8On\":\"Tour completion confirmed successfully\",\"ghwok7\":\"Two factor code is incorrect. Please try again.\",\"to2nZU\":\"Two factor code length is incorrect. Please try again.\",\"4G8XnK\":\"Unable leave organization. Please try again.\",\"VtK4v3\":\"Unable to add domains in unknown organization.\",\"5c9XSu\":\"Unable to archive organization. Please try again.\",\"d0IZcx\":\"Unable to archive unknown organization.\",\"iGBFKx\":\"Unable to authenticate. Please try again.\",\"hGYodY\":\"Unable to check permission. Please try again.\",\"JBqBmK\":\"Unable to close account of an undefined user.\",\"ASvI35\":\"Unable to close account. Please try again.\",\"lYCFPq\":\"Unable to confirm completion of the tour. Please try again.\",\"JH9/Jq\":\"Unable to create domain in unknown organization.\",\"TUFqYc\":\"Unable to create domain, organization has already claimed it.\",\"PnTl0X\":\"Unable to create domain. Please try again.\",\"VkUlo7\":\"Unable to create domains. Please try again.\",\"Bk0F/3\":\"Unable to create organization. Please try again.\",\"ggS9AU\":\"Unable to discover domains for unknown organization.\",\"HLFEUv\":\"Unable to dismiss message. Please try again.\",\"U8WJkK\":\"Unable to dispatch one time scan. Please try again.\",\"sX31Qs\":\"Unable to export organization. Please try again.\",\"QDd6b7\":\"Unable to favourite domain, user has already favourited it.\",\"+4vwFL\":\"Unable to favourite domain. Please try again.\",\"WNzPnw\":\"Unable to favourite unknown domain.\",\"lPIuFw\":\"Unable to find Aggregate guidance tag(s). Please try again.\",\"q4hBrf\":\"Unable to find DKIM guidance tag(s). Please try again.\",\"BC7dKY\":\"Unable to find DKIM result(s). Please try again.\",\"C5fP4H\":\"Unable to find DKIM scan(s). Please try again.\",\"GH27F7\":\"Unable to find DMARC guidance tag(s). Please try again.\",\"ngV4gO\":\"Unable to find DMARC scan(s). Please try again.\",\"GtHb3j\":\"Unable to find DMARC summary data. Please try again.\",\"ULGdf/\":\"Unable to find DNS scan(s). Please try again.\",\"kzp3/l\":\"Unable to find guidance tag(s). Please try again.\",\"lvuEwe\":\"Unable to find HTTPS guidance tag(s). Please try again.\",\"eEnZq2\":\"Unable to find HTTPS scan(s). Please try again.\",\"8UI/ez\":\"Unable to find SPF guidance tag(s). Please try again.\",\"EJbuMu\":\"Unable to find SPF scan(s). Please try again.\",\"jy/9PC\":\"Unable to find SSL guidance tag(s). Please try again.\",\"xsn+ev\":\"Unable to find SSL scan(s). Please try again.\",\"itNwLs\":\"Unable to find the requested domain.\",\"AzNyVV\":\"Unable to find user affiliation(s). Please try again.\",\"usyHv4\":\"Unable to find verified organization(s). Please try again.\",\"J0A2iO\":\"Unable to ignore CVE. Please try again.\",\"QlRbEs\":\"Unable to invite user to organization. Please try again.\",\"Y3CReu\":\"Unable to invite user to organization. User is already affiliated with organization.\",\"4YaHho\":\"Unable to invite user to unknown organization.\",\"6DMjZ8\":\"Unable to invite user. Please try again.\",\"mFUvni\":\"Unable to invite yourself to an org.\",\"RuV5QP\":\"Unable to leave organization. Please try again.\",\"pAAJtC\":\"Unable to leave undefined organization.\",\"NRzPen\":\"Unable to load additional findings. Please try again.\",\"Q2fyq2\":\"Unable to load affiliation information. Please try again.\",\"dLE6X6\":\"Unable to load affiliation(s). Please try again.\",\"tEuM0Y\":\"Unable to load Aggregate guidance tag(s). Please try again.\",\"z6WNrg\":\"Unable to load all organization domain statuses. Please try again.\",\"AS+XYm\":\"Unable to load chart summary data. Please try again.\",\"VZSEkv\":\"Unable to load DKIM failure data. Please try again.\",\"iHCZPz\":\"Unable to load DKIM guidance tag(s). Please try again.\",\"VYgxhl\":\"Unable to load DKIM result(s). Please try again.\",\"XmHD0n\":\"Unable to load DKIM scan(s). Please try again.\",\"H/foxs\":\"Unable to load DKIM summary. Please try again.\",\"DZfpKQ\":\"Unable to load DMARC failure data. Please try again.\",\"IWleX/\":\"Unable to load DMARC guidance tag(s). Please try again.\",\"9KOsEh\":\"Unable to load DMARC phase summary. Please try again.\",\"eNPs30\":\"Unable to load DMARC scan(s). Please try again.\",\"wKC+/J\":\"Unable to load DMARC summary data. Please try again.\",\"PF8L8T\":\"Unable to load DMARC summary. Please try again.\",\"zv2kI4\":\"Unable to load DNS scan(s). Please try again.\",\"+mdFBt\":\"Unable to load domain selector(s). Please try again.\",\"hpeR8m\":\"Unable to load domain. Please try again.\",\"f/tKr7\":\"Unable to load domain(s). Please try again.\",\"aHKeg4\":\"Unable to load full pass data. Please try again.\",\"3fphqj\":\"Unable to load guidance tag(s). Please try again.\",\"cCar8N\":\"Unable to load HTTPS guidance tag(s). Please try again.\",\"7A73DJ\":\"Unable to load HTTPS scan(s). Please try again.\",\"BMKKFn\":\"Unable to load HTTPS summary. Please try again.\",\"hZEouI\":\"Unable to load log. Please try again.\",\"lSzsy0\":\"Unable to load log(s). Please try again.\",\"wrwKt2\":\"Unable to load mail summary. Please try again.\",\"Fpm2AP\":\"Unable to load organization domain statuses. Please try again.\",\"p6Lm32\":\"Unable to load organization summary data. Please try again.\",\"8KT8ti\":\"Unable to load organization(s). Please try again.\",\"LvHBFL\":\"Unable to load owner information. Please try again.\",\"sXOtBn\":\"Unable to load SPF failure data. Please try again.\",\"IQkbNl\":\"Unable to load SPF guidance tag(s). Please try again.\",\"FHxRJZ\":\"Unable to load SPF scan(s). Please try again.\",\"ZsscmB\":\"Unable to load SPF summary. Please try again.\",\"HI7CSw\":\"Unable to load SSL guidance tag(s). Please try again.\",\"tJYOji\":\"Unable to load SSL scan(s). Please try again.\",\"PpwpCp\":\"Unable to load SSL summary. Please try again.\",\"xzKtay\":\"Unable to load summary. Please try again.\",\"y7ER1b\":\"Unable to load tags(s). Please try again.\",\"PzFOAc\":\"Unable to load user(s). Please try again.\",\"Ph65og\":\"Unable to load verified domain(s). Please try again.\",\"QdFjoo\":\"Unable to load verified organization(s). Please try again.\",\"MjZQ9y\":\"Unable to load verified rua domains. Please try again.\",\"WMM6DC\":\"Unable to load web connections summary. Please try again.\",\"szMW1T\":\"Unable to load web scan(s). Please try again.\",\"OEKyZJ\":\"Unable to load web summary. Please try again.\",\"j4fHlS\":\"Unable to query affiliation(s). Please try again.\",\"c0mbW+\":\"Unable to query domain(s). Please try again.\",\"7W4MNs\":\"Unable to query log(s). Please try again.\",\"098Gys\":\"Unable to query user(s). Please try again.\",\"KfROBu\":\"Unable to refresh tokens, please sign in.\",\"Hls3tJ\":\"Unable to remove a user that already does not belong to this organization.\",\"ELijnP\":\"Unable to remove domain from unknown organization.\",\"nuxrPH\":\"Unable to remove domain. Domain is not part of organization.\",\"hHXji3\":\"Unable to remove domain. Please try again.\",\"iMzlxt\":\"Unable to remove domains from unknown organization.\",\"X34ZOQ\":\"Unable to remove organization. Please try again.\",\"Qf8OvV\":\"Unable to remove phone number. Please try again.\",\"GXX5w7\":\"Unable to remove unknown domain.\",\"pQ0cLH\":\"Unable to remove unknown organization.\",\"gWw+wU\":\"Unable to remove unknown user from organization.\",\"APaKFI\":\"Unable to remove user from organization.\",\"v28SUT\":\"Unable to remove user from this organization. Please try again.\",\"qjq1fL\":\"Unable to remove user from unknown organization.\",\"Wu3IdK\":\"Unable to request a one time scan on a domain that already has a pending scan.\",\"s79a/Q\":\"Unable to request a one time scan on an unknown domain.\",\"BlpeBr\":\"Unable to request a one time scan. Please try again.\",\"YlWZ/S\":\"Unable to request a subdomain discovery scan on an invalid domain.\",\"/rRkGm\":\"Unable to request a subdomain discovery scan on an unknown domain.\",\"qTisBR\":\"Unable to request invite to organization with which you are already affiliated.\",\"1+X6yw\":\"Unable to request invite to organization with which you have already requested to join.\",\"Djnfqd\":\"Unable to request invite to unknown organization.\",\"LlyhW+\":\"Unable to request invite. Please try again.\",\"NQWBIw\":\"Unable to reset password. Please request a new email.\",\"dJ2lzx\":\"Unable to reset password. Please try again.\",\"t2wESI\":[\"Unable to retrieve DMARC report information for: \",[\"domain\"]],\"OTycVQ\":\"Unable to select DMARC report(s) for this period and year.\",\"qT4MFW\":\"Unable to send authentication email. Please try again.\",\"LPodG6\":\"Unable to send authentication text message. Please try again.\",\"9TsNGX\":\"Unable to send org invite email. Please try again.\",\"zwqufn\":\"Unable to send org invite request email. Please try again.\",\"1rL9I7\":\"Unable to send password reset email. Please try again.\",\"svsSOK\":\"Unable to send two factor authentication message. Please try again.\",\"ujyB0i\":\"Unable to send updated username email. Please try again.\",\"02Keb+\":\"Unable to send verification email. Please try again.\",\"E4JeKk\":\"Unable to set phone number, please try again.\",\"g3Svfo\":\"Unable to sign in, please try again.\",\"VqhI6Q\":\"Unable to sign up, please contact org admin for a new invite.\",\"2hEUmM\":\"Unable to sign up. Please try again.\",\"7/HZU+\":\"Unable to stop ignoring CVE. Please try again.\",\"JLEqkA\":\"Unable to transfer organization ownership. Please try again.\",\"U1j4ef\":\"Unable to transfer ownership of a verified organization.\",\"c3WjG6\":\"Unable to transfer ownership of an org to an undefined user.\",\"TMjnAo\":\"Unable to transfer ownership of undefined organization.\",\"rzoBBf\":\"Unable to transfer ownership to a user outside the org. Please invite the user and try again.\",\"8TSaAb\":\"Unable to two factor authenticate. Please try again.\",\"FHUz/I\":\"Unable to unfavourite domain, domain is not favourited.\",\"F7RRVM\":\"Unable to unfavourite domain. Please try again.\",\"RgT2Fw\":\"Unable to unfavourite unknown domain.\",\"iM9uiT\":\"Unable to unignore CVE. Please try again.\",\"CmZgVA\":\"Unable to update domain edge. Please try again.\",\"gAMNI6\":\"Unable to update domain in an unknown org.\",\"JbEMFm\":\"Unable to update domain that does not belong to the given organization.\",\"jHfpyF\":\"Unable to update domain. Please try again.\",\"0tE9dW\":\"Unable to update organization. Please try again.\",\"4Dutt5\":\"Unable to update password, current password does not match. Please try again.\",\"fNY+xk\":\"Unable to update password, new passwords do not match. Please try again.\",\"TK5yIB\":\"Unable to update password, passwords do not match requirements. Please try again.\",\"2OQWjx\":\"Unable to update password. Please try again.\",\"+q5Rwg\":\"Unable to update profile. Please try again.\",\"Zqql23\":\"Unable to update role: organization unknown.\",\"5g5CPf\":\"Unable to update role: user does not belong to organization.\",\"ygexeT\":\"Unable to update role: user unknown.\",\"K5a9HQ\":\"Unable to update unknown domain.\",\"4WmaEA\":\"Unable to update unknown organization.\",\"13kdVN\":\"Unable to update user's role. Please try again.\",\"xWavp5\":\"Unable to update your own role.\",\"Vs/Ux1\":\"Unable to verify account. Please request a new email.\",\"Z4+72s\":\"Unable to verify account. Please try again.\",\"Tqm26J\":\"Unable to verify if user is a super admin, please try again.\",\"KPWee0\":\"Unable to verify if user is an admin, please try again.\",\"apJs/U\":\"Unable to verify organization. Please try again.\",\"x5sf5T\":\"Unable to verify unknown organization.\",\"sesHCz\":\"User could not be queried.\",\"czGsbE\":\"User is trying to register for a non-production environment.\",\"Tgpvqi\":\"User role was updated successfully.\",\"iuCjUb\":\"Username not available, please try another.\",\"V/PS9h\":\"Verification error. Please activate multi-factor authentication to access content.\",\"7zQuGi\":\"Verification error. Please verify your account via email to access content.\",\"kMaiy6\":\"You must provide a `domainId` to retrieve a domain's additional findings.\",\"p+DYLk\":\"You must provide a `first` or `last` value to properly paginate the `Affiliation` connection.\",\"c7s2Nw\":\"You must provide a `first` or `last` value to properly paginate the `DKIM` connection.\",\"SEEDiW\":\"You must provide a `first` or `last` value to properly paginate the `DkimFailureTable` connection.\",\"wlbfWc\":\"You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection.\",\"lu3LE3\":\"You must provide a `first` or `last` value to properly paginate the `DMARC` connection.\",\"pDllMl\":\"You must provide a `first` or `last` value to properly paginate the `DmarcFailureTable` connection.\",\"voJfyc\":\"You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection.\",\"c8Qbmn\":\"You must provide a `first` or `last` value to properly paginate the `Domain` connection.\",\"ZacidG\":\"You must provide a `first` or `last` value to properly paginate the `FullPassTable` connection.\",\"mciz5o\":\"You must provide a `first` or `last` value to properly paginate the `GuidanceTag` connection.\",\"87Zz6g\":\"You must provide a `first` or `last` value to properly paginate the `HTTPS` connection.\",\"co0onA\":\"You must provide a `first` or `last` value to properly paginate the `Log` connection.\",\"thP+Yz\":\"You must provide a `first` or `last` value to properly paginate the `Organization` connection.\",\"Xfjual\":\"You must provide a `first` or `last` value to properly paginate the `SPF` connection.\",\"4s6W/Q\":\"You must provide a `first` or `last` value to properly paginate the `SpfFailureTable` connection.\",\"EsdwHf\":\"You must provide a `first` or `last` value to properly paginate the `SSL` connection.\",\"N+og2p\":\"You must provide a `first` or `last` value to properly paginate the `User` connection.\",\"EMHIWF\":\"You must provide a `first` or `last` value to properly paginate the `VerifiedDomain` connection.\",\"RMtxoi\":\"You must provide a `first` or `last` value to properly paginate the `VerifiedOrganization` connection.\",\"3Zdrst\":\"You must provide a `limit` value in the range of 1-100 to properly paginate the `DNS` connection.\",\"DRToPU\":\"You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection.\",\"NfXI/l\":\"You must provide a `limit` value in the range of 1-100 to properly paginate the `web` connection.\",\"KAZHf6\":\"You must provide a `limit` value to properly paginate the `DNS` connection.\",\"uTt7kN\":\"You must provide a `limit` value to properly paginate the `MXRecord` connection.\",\"KyJdrc\":\"You must provide a `limit` value to properly paginate the `web` connection.\",\"TAJGTA\":\"You must provide a `period` value to access the `ChartSummaries` connection.\",\"DWa4my\":\"You must provide a `period` value to access the `DmarcSummaries` connection.\",\"XZZ7Lv\":\"You must provide a `period` value to access the `OrganizationSummaries` connection.\",\"oafMGl\":\"You must provide a `year` value to access the `ChartSummaries` connection.\",\"jqcUEU\":\"You must provide a `year` value to access the `DmarcSummaries` connection.\",\"z4RkNn\":\"You must provide a `year` value to access the `OrganizationSummaries` connection.\",\"JOssnw\":\"You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `DNS` connection.\",\"cfpKpu\":\"You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection.\",\"O++odO\":\"You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `web` connection.\"}")}; \ No newline at end of file diff --git a/api/src/locale/en/messages.po b/api/src/locale/en/messages.po index 8cd5e37f1..a2b0b826b 100644 --- a/api/src/locale/en/messages.po +++ b/api/src/locale/en/messages.po @@ -11,75 +11,226 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#: src/auth/check-permission.js:18 -#: src/auth/check-permission.js:57 -#: src/auth/user-required.js:10 -#: src/auth/user-required.js:21 -#: src/auth/user-required.js:28 +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:121 +#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:208 +#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:131 +#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:83 +#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:83 +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:202 +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:83 +#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:83 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:178 +#: src/domain/loaders/load-domain-connections-by-user-id.js:205 +#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:132 +#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:136 +#: src/organization/loaders/load-organization-connections-by-domain-id.js:203 +#: src/organization/loaders/load-organization-connections-by-user-id.js:213 +#: src/user/loaders/load-user-connections-by-user-id.js:145 +#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:164 +#: src/verified-domains/loaders/load-verified-domain-connections.js:164 +#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:214 +#: src/verified-organizations/loaders/load-verified-organizations-connections.js:212 +msgid "`{argSet}` must be of type `number` not `{typeSet}`." +msgstr "`{argSet}` must be of type `number` not `{typeSet}`." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:98 +#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:182 +msgid "`{argSet}` on the `Affiliation` connection cannot be less than zero." +msgstr "`{argSet}` on the `Affiliation` connection cannot be less than zero." + +#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:135 +#~ msgid "`{argSet}` on the `DKIM` connection cannot be less than zero." +#~ msgstr "`{argSet}` on the `DKIM` connection cannot be less than zero." + +#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `DkimFailureTable` connection cannot be less than zero." +msgstr "`{argSet}` on the `DkimFailureTable` connection cannot be less than zero." + +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:110 +#~ msgid "`{argSet}` on the `DKIMResults` connection cannot be less than zero." +#~ msgstr "`{argSet}` on the `DKIMResults` connection cannot be less than zero." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:160 +#~ msgid "`{argSet}` on the `DMARC` connection cannot be less than zero." +#~ msgstr "`{argSet}` on the `DMARC` connection cannot be less than zero." + +#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero." +msgstr "`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero." + +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:179 +msgid "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." +msgstr "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." + +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:157 +#: src/domain/loaders/load-domain-connections-by-user-id.js:182 +msgid "`{argSet}` on the `Domain` connection cannot be less than zero." +msgstr "`{argSet}` on the `Domain` connection cannot be less than zero." + +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `FullPassTable` connection cannot be less than zero." +msgstr "`{argSet}` on the `FullPassTable` connection cannot be less than zero." + +#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:106 +#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:110 +msgid "`{argSet}` on the `GuidanceTag` connection cannot be less than zero." +msgstr "`{argSet}` on the `GuidanceTag` connection cannot be less than zero." + +#: src/web-scan/loaders/load-https-connections-by-domain-id.js:168 +#~ msgid "`{argSet}` on the `HTTPS` connection cannot be less than zero." +#~ msgstr "`{argSet}` on the `HTTPS` connection cannot be less than zero." + +#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:112 +msgid "`{argSet}` on the `Log` connection cannot be less than zero." +msgstr "`{argSet}` on the `Log` connection cannot be less than zero." + +#: src/organization/loaders/load-organization-connections-by-domain-id.js:182 +#: src/organization/loaders/load-organization-connections-by-user-id.js:192 +msgid "`{argSet}` on the `Organization` connection cannot be less than zero." +msgstr "`{argSet}` on the `Organization` connection cannot be less than zero." + +#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:154 +#~ msgid "`{argSet}` on the `SPF` connection cannot be less than zero." +#~ msgstr "`{argSet}` on the `SPF` connection cannot be less than zero." + +#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `SpfFailureTable` connection cannot be less than zero." +msgstr "`{argSet}` on the `SpfFailureTable` connection cannot be less than zero." + +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:192 +#~ msgid "`{argSet}` on the `SSL` connection cannot be less than zero." +#~ msgstr "`{argSet}` on the `SSL` connection cannot be less than zero." + +#: src/user/loaders/load-user-connections-by-user-id.js:122 +msgid "`{argSet}` on the `User` connection cannot be less than zero." +msgstr "`{argSet}` on the `User` connection cannot be less than zero." + +#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:138 +#: src/verified-domains/loaders/load-verified-domain-connections.js:138 +msgid "`{argSet}` on the `VerifiedDomain` connection cannot be less than zero." +msgstr "`{argSet}` on the `VerifiedDomain` connection cannot be less than zero." + +#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:188 +#: src/verified-organizations/loaders/load-verified-organizations-connections.js:186 +msgid "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." +msgstr "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." + +#: src/organization/objects/organization.js:240 +#: src/organization/queries/get-all-organization-domain-statuses.js:69 +msgid "Assess" +msgstr "Assess" + +#: src/auth/checks/check-permission.js:18 +#: src/auth/checks/check-permission.js:57 +#: src/auth/guards/user-required.js:10 +#: src/auth/guards/user-required.js:21 +#: src/auth/guards/user-required.js:28 +#: src/auth/loaders/load-permission-by-org-id.js:19 +#: src/auth/loaders/load-permission-by-org-id.js:63 msgid "Authentication error. Please sign in." msgstr "Authentication error. Please sign in." -#: src/organization/objects/organization.js:296 +#: src/domain/objects/domain.js:229 +msgid "Cannot query additional findings without permission." +msgstr "Cannot query additional findings without permission." + +#: src/organization/objects/organization.js:359 msgid "Cannot query affiliations on organization without admin permission or higher." msgstr "Cannot query affiliations on organization without admin permission or higher." #: src/audit-logs/loaders/load-audit-logs-by-org-id.js:224 -#: src/audit-logs/queries/find-audit-logs.js:51 +#: src/audit-logs/queries/find-audit-logs.js:53 msgid "Cannot query audit logs on organization without admin permission or higher." msgstr "Cannot query audit logs on organization without admin permission or higher." -#: src/domain/objects/domain.js:161 +#: src/domain/objects/domain.js:164 msgid "Cannot query dns scan results without permission." msgstr "Cannot query dns scan results without permission." -#: src/domain/objects/domain.js:63 +#: src/domain/objects/domain.js:65 msgid "Cannot query domain selectors without permission." msgstr "Cannot query domain selectors without permission." -#: src/domain/objects/domain.js:203 -#: src/domain/objects/domain.js:249 +#: src/domain/objects/domain.js:206 msgid "Cannot query web scan results without permission." msgstr "Cannot query web scan results without permission." -#: src/user/mutations/sign-up.js:104 +#: src/domain/mutations/ignore-cve.js:77 +msgid "CVE is already ignored for this domain." +msgstr "CVE is already ignored for this domain." + +#: src/domain/mutations/unignore-cve.js:77 +msgid "CVE is not ignored for this domain." +msgstr "CVE is not ignored for this domain." + +#: src/organization/objects/organization.js:242 +#: src/organization/queries/get-all-organization-domain-statuses.js:71 +msgid "Deploy" +msgstr "Deploy" + +#: src/user/mutations/sign-up.js:111 msgid "Email already in use." msgstr "Email already in use." -#: src/domain/mutations/request-scan.js:87 -#: src/domain/mutations/request-scan.js:97 +#: src/organization/objects/organization.js:244 +#: src/organization/queries/get-all-organization-domain-statuses.js:73 +msgid "Enforce" +msgstr "Enforce" + +#: src/domain/mutations/request-scan.js:90 +#: src/domain/mutations/request-scan.js:100 msgid "Error while requesting scan. Please try again." msgstr "Error while requesting scan. Please try again." -#: src/user/mutations/send-password-reset.js:57 +#: src/user/mutations/send-password-reset.js:61 msgid "If an account with this username is found, a password reset link will be found in your inbox." msgstr "If an account with this username is found, a password reset link will be found in your inbox." -#: src/user/mutations/send-email-verification.js:59 -msgid "If an account with this username is found, an email verification link will be found in your inbox." -msgstr "If an account with this username is found, an email verification link will be found in your inbox." +#: src/user/mutations/send-email-verification.js:60 +#~ msgid "If an account with this username is found, an email verification link will be found in your inbox." +#~ msgstr "If an account with this username is found, an email verification link will be found in your inbox." -#: src/user/mutations/authenticate.js:186 +#: src/user/mutations/authenticate.js:221 +#: src/user/mutations/authenticate.js:229 +#: src/user/mutations/authenticate.js:231 msgid "Incorrect TFA code. Please sign in again." msgstr "Incorrect TFA code. Please sign in again." -#: src/user/mutations/reset-password.js:59 +#: src/user/mutations/reset-password.js:61 msgid "Incorrect token value. Please request a new email." msgstr "Incorrect token value. Please request a new email." -#: src/user/mutations/sign-in.js:66 -#: src/user/mutations/sign-in.js:260 +#: src/user/mutations/sign-in.js:68 +#: src/user/mutations/sign-in.js:285 msgid "Incorrect username or password. Please try again." msgstr "Incorrect username or password. Please try again." -#: src/auth/verify-jwt.js:15 +#: src/auth/utils/verify-jwt.js:15 msgid "Invalid token, please sign in." msgstr "Invalid token, please sign in." -#: src/user/mutations/reset-password.js:98 +#: src/organization/objects/organization.js:246 +#: src/organization/queries/get-all-organization-domain-statuses.js:75 +msgid "Maintain" +msgstr "Maintain" + +#: src/user/mutations/dismiss-message.js:75 +msgid "Message dismissed successfully" +msgstr "Message dismissed successfully" + +#: src/user/mutations/reset-password.js:88 msgid "New passwords do not match." msgstr "New passwords do not match." -#: src/organization/queries/find-organization-by-slug.js:41 +#: src/organization/queries/find-organization-by-slug.js:42 #: src/user/queries/find-my-tracker.js:29 msgid "No organization with the provided slug could be found." msgstr "No organization with the provided slug could be found." @@ -92,47 +243,47 @@ msgstr "No verified domain with the provided domain could be found." msgid "No verified organization with the provided slug could be found." msgstr "No verified organization with the provided slug could be found." -#: src/organization/mutations/verify-organization.js:81 +#: src/organization/mutations/verify-organization.js:78 msgid "Organization has already been verified." msgstr "Organization has already been verified." -#: src/organization/mutations/update-organization.js:174 +#: src/organization/mutations/update-organization.js:167 msgid "Organization name already in use, please choose another and try again." msgstr "Organization name already in use, please choose another and try again." -#: src/organization/mutations/create-organization.js:124 +#: src/organization/mutations/create-organization.js:85 msgid "Organization name already in use. Please try again with a different name." msgstr "Organization name already in use. Please try again with a different name." -#: src/auth/check-domain-ownership.js:29 -#: src/auth/check-domain-ownership.js:39 -#: src/auth/check-domain-ownership.js:65 -#: src/auth/check-domain-ownership.js:74 +#: src/auth/checks/check-domain-ownership.js:29 +#: src/auth/checks/check-domain-ownership.js:39 +#: src/auth/checks/check-domain-ownership.js:65 +#: src/auth/checks/check-domain-ownership.js:74 msgid "Ownership check error. Unable to request domain information." msgstr "Ownership check error. Unable to request domain information." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:77 +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:89 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:170 msgid "Passing both `first` and `last` to paginate the `Affiliation` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `Affiliation` connection is not supported." -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:98 -#~ msgid "Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported." -#~ msgstr "Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported." - #: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:123 #: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:159 #~ msgid "Passing both `first` and `last` to paginate the `DKIM` connection is not supported." #~ msgstr "Passing both `first` and `last` to paginate the `DKIM` connection is not supported." -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:147 -#~ msgid "Passing both `first` and `last` to paginate the `DMARC` connection is not supported." -#~ msgstr "Passing both `first` and `last` to paginate the `DMARC` connection is not supported." - #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:45 msgid "Passing both `first` and `last` to paginate the `DkimFailureTable` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `DkimFailureTable` connection is not supported." +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:98 +#~ msgid "Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported." +#~ msgstr "Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:147 +#~ msgid "Passing both `first` and `last` to paginate the `DMARC` connection is not supported." +#~ msgstr "Passing both `first` and `last` to paginate the `DMARC` connection is not supported." + #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:45 msgid "Passing both `first` and `last` to paginate the `DmarcFailureTable` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `DmarcFailureTable` connection is not supported." @@ -142,7 +293,7 @@ msgid "Passing both `first` and `last` to paginate the `DmarcSummaries` connecti msgstr "Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported." #: src/domain/loaders/load-domain-connections-by-organizations-id.js:148 -#: src/domain/loaders/load-domain-connections-by-user-id.js:160 +#: src/domain/loaders/load-domain-connections-by-user-id.js:173 msgid "Passing both `first` and `last` to paginate the `Domain` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `Domain` connection is not supported." @@ -168,7 +319,7 @@ msgid "Passing both `first` and `last` to paginate the `Log` connection is not s msgstr "Passing both `first` and `last` to paginate the `Log` connection is not supported." #: src/organization/loaders/load-organization-connections-by-domain-id.js:173 -#: src/organization/loaders/load-organization-connections-by-user-id.js:173 +#: src/organization/loaders/load-organization-connections-by-user-id.js:185 msgid "Passing both `first` and `last` to paginate the `Organization` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `Organization` connection is not supported." @@ -176,15 +327,15 @@ msgstr "Passing both `first` and `last` to paginate the `Organization` connectio #~ msgid "Passing both `first` and `last` to paginate the `SPF` connection is not supported." #~ msgstr "Passing both `first` and `last` to paginate the `SPF` connection is not supported." -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:180 -#~ msgid "Passing both `first` and `last` to paginate the `SSL` connection is not supported." -#~ msgstr "Passing both `first` and `last` to paginate the `SSL` connection is not supported." - #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:45 msgid "Passing both `first` and `last` to paginate the `SpfFailureTable` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `SpfFailureTable` connection is not supported." -#: src/user/loaders/load-user-connections-by-user-id.js:116 +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:180 +#~ msgid "Passing both `first` and `last` to paginate the `SSL` connection is not supported." +#~ msgstr "Passing both `first` and `last` to paginate the `SSL` connection is not supported." + +#: src/user/loaders/load-user-connections-by-user-id.js:113 msgid "Passing both `first` and `last` to paginate the `User` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `User` connection is not supported." @@ -198,29 +349,38 @@ msgstr "Passing both `first` and `last` to paginate the `VerifiedDomain` connect msgid "Passing both `first` and `last` to paginate the `VerifiedOrganization` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `VerifiedOrganization` connection is not supported." -#: src/user/mutations/reset-password.js:110 -#: src/user/mutations/sign-up.js:82 +#: src/user/mutations/reset-password.js:100 +#: src/user/mutations/sign-up.js:89 msgid "Password does not meet requirements." msgstr "Password does not meet requirements." -#: src/user/mutations/reset-password.js:148 +#: src/user/mutations/reset-password.js:140 msgid "Password was successfully reset." msgstr "Password was successfully reset." -#: src/user/mutations/update-user-password.js:107 +#: src/user/mutations/update-user-password.js:109 msgid "Password was successfully updated." msgstr "Password was successfully updated." -#: src/user/mutations/sign-up.js:92 +#: src/user/mutations/sign-up.js:99 msgid "Passwords do not match." msgstr "Passwords do not match." +#: src/auth/checks/check-domain-permission.js:22 +#: src/auth/checks/check-domain-permission.js:51 +#: src/auth/checks/check-domain-permission.js:61 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:19 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:51 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:61 +msgid "Permission check error. Unable to request domain information." +msgstr "Permission check error. Unable to request domain information." + #: src/organization/queries/find-organization-by-slug.js:50 -#: src/organization/queries/find-organization-by-slug.js:55 +#: src/organization/queries/find-organization-by-slug.js:52 msgid "Permission Denied: Could not retrieve specified organization." msgstr "Permission Denied: Could not retrieve specified organization." -#: src/user/mutations/update-user-profile.js:110 +#: src/user/mutations/update-user-profile.js:109 msgid "Permission Denied: Multi-factor authentication is required for admin accounts" msgstr "Permission Denied: Multi-factor authentication is required for admin accounts" @@ -228,79 +388,99 @@ msgstr "Permission Denied: Multi-factor authentication is required for admin acc msgid "Permission Denied: Please contact org owner to transfer ownership." msgstr "Permission Denied: Please contact org owner to transfer ownership." -#: src/domain/mutations/remove-organizations-domains.js:127 +#: src/domain/mutations/remove-organizations-domains.js:128 msgid "Permission Denied: Please contact organization admin for help with archiving domains." msgstr "Permission Denied: Please contact organization admin for help with archiving domains." -#: src/domain/mutations/remove-domain.js:94 +#: src/tags/mutations/create-tag.js:131 +msgid "Permission Denied: Please contact organization admin for help with creating tag." +msgstr "Permission Denied: Please contact organization admin for help with creating tag." + +#: src/domain/mutations/remove-domain.js:96 msgid "Permission Denied: Please contact organization admin for help with removing domain." msgstr "Permission Denied: Please contact organization admin for help with removing domain." -#: src/domain/mutations/remove-organizations-domains.js:116 +#: src/domain/mutations/remove-organizations-domains.js:117 msgid "Permission Denied: Please contact organization admin for help with removing domains." msgstr "Permission Denied: Please contact organization admin for help with removing domains." -#: src/organization/mutations/remove-organization.js:69 +#: src/organization/mutations/remove-organization.js:67 msgid "Permission Denied: Please contact organization admin for help with removing organization." msgstr "Permission Denied: Please contact organization admin for help with removing organization." -#: src/affiliation/mutations/remove-user-from-org.js:126 -#: src/affiliation/mutations/remove-user-from-org.js:138 +#: src/affiliation/mutations/remove-user-from-org.js:128 +#: src/affiliation/mutations/remove-user-from-org.js:140 msgid "Permission Denied: Please contact organization admin for help with removing users." msgstr "Permission Denied: Please contact organization admin for help with removing users." -#: src/organization/mutations/update-organization.js:145 +#: src/domain/mutations/update-domains-by-domain-ids.js:81 +#: src/domain/mutations/update-domains-by-filters.js:89 +msgid "Permission Denied: Please contact organization admin for help with updating domains." +msgstr "Permission Denied: Please contact organization admin for help with updating domains." + +#: src/organization/mutations/update-organization.js:152 msgid "Permission Denied: Please contact organization admin for help with updating organization." msgstr "Permission Denied: Please contact organization admin for help with updating organization." +#: src/tags/mutations/update-tag.js:125 +#: src/tags/mutations/update-tag.js:136 +msgid "Permission Denied: Please contact organization admin for help with updating tag." +msgstr "Permission Denied: Please contact organization admin for help with updating tag." + #: src/affiliation/mutations/update-user-role.js:170 #: src/affiliation/mutations/update-user-role.js:193 #: src/affiliation/mutations/update-user-role.js:210 #~ msgid "Permission Denied: Please contact organization admin for help with updating user roles." #~ msgstr "Permission Denied: Please contact organization admin for help with updating user roles." -#: src/affiliation/mutations/invite-user-to-org.js:101 +#: src/affiliation/mutations/invite-user-to-org.js:99 msgid "Permission Denied: Please contact organization admin for help with user invitations." msgstr "Permission Denied: Please contact organization admin for help with user invitations." -#: src/affiliation/mutations/update-user-role.js:109 -#: src/affiliation/mutations/update-user-role.js:160 -#: src/affiliation/mutations/update-user-role.js:172 +#: src/affiliation/mutations/update-user-role.js:112 msgid "Permission Denied: Please contact organization admin for help with user role changes." msgstr "Permission Denied: Please contact organization admin for help with user role changes." -#: src/domain/mutations/create-domain.js:145 +#: src/domain/mutations/create-domain.js:135 msgid "Permission Denied: Please contact organization user for help with creating domain." msgstr "Permission Denied: Please contact organization user for help with creating domain." -#: src/domain/mutations/add-organizations-domains.js:125 +#: src/domain/mutations/add-organizations-domains.js:122 msgid "Permission Denied: Please contact organization user for help with creating domains." msgstr "Permission Denied: Please contact organization user for help with creating domains." +#: src/organization/objects/organization.js:111 +#~ msgid "Permission Denied: Please contact organization user for help with retrieving tags." +#~ msgstr "Permission Denied: Please contact organization user for help with retrieving tags." + #: src/domain/queries/find-domain-by-domain.js:51 -#: src/organization/objects/organization.js:144 +#: src/organization/objects/organization.js:195 msgid "Permission Denied: Please contact organization user for help with retrieving this domain." msgstr "Permission Denied: Please contact organization user for help with retrieving this domain." -#: src/domain/mutations/request-discovery.js:97 -#: src/domain/mutations/request-scan.js:63 +#: src/domain/mutations/request-discovery.js:98 +#: src/domain/mutations/request-scan.js:66 msgid "Permission Denied: Please contact organization user for help with scanning this domain." msgstr "Permission Denied: Please contact organization user for help with scanning this domain." -#: src/domain/mutations/update-domain.js:163 +#: src/domain/mutations/update-domain.js:162 msgid "Permission Denied: Please contact organization user for help with updating this domain." msgstr "Permission Denied: Please contact organization user for help with updating this domain." -#: src/organization/mutations/archive-organization.js:68 +#: src/organization/mutations/archive-organization.js:66 msgid "Permission Denied: Please contact super admin for help with archiving organization." msgstr "Permission Denied: Please contact super admin for help with archiving organization." -#: src/domain/mutations/remove-domain.js:107 -#: src/domain/mutations/remove-organizations-domains.js:105 +#: src/domain/mutations/create-domain.js:157 +msgid "Permission Denied: Please contact super admin for help with creating domain." +msgstr "Permission Denied: Please contact super admin for help with creating domain." + +#: src/domain/mutations/remove-domain.js:109 +#: src/domain/mutations/remove-organizations-domains.js:106 msgid "Permission Denied: Please contact super admin for help with removing domain." msgstr "Permission Denied: Please contact super admin for help with removing domain." -#: src/organization/mutations/remove-organization.js:82 +#: src/organization/mutations/remove-organization.js:80 msgid "Permission Denied: Please contact super admin for help with removing organization." msgstr "Permission Denied: Please contact super admin for help with removing organization." @@ -308,50 +488,83 @@ msgstr "Permission Denied: Please contact super admin for help with removing org #~ msgid "Permission Denied: Please contact super admin for help with scanning this domain." #~ msgstr "Permission Denied: Please contact super admin for help with scanning this domain." -#: src/organization/mutations/verify-organization.js:68 +#: src/domain/mutations/update-domain.js:210 +msgid "Permission Denied: Please contact super admin for help with updating domain." +msgstr "Permission Denied: Please contact super admin for help with updating domain." + +#: src/tags/mutations/update-tag.js:147 +msgid "Permission Denied: Please contact super admin for help with updating tag." +msgstr "Permission Denied: Please contact super admin for help with updating tag." + +#: src/affiliation/mutations/invite-user-to-org.js:112 +msgid "Permission Denied: Please contact super admin for help with user invitations." +msgstr "Permission Denied: Please contact super admin for help with user invitations." + +#: src/affiliation/mutations/update-user-role.js:167 +msgid "Permission Denied: Please contact super admin for help with user role changes." +msgstr "Permission Denied: Please contact super admin for help with user role changes." + +#: src/organization/mutations/verify-organization.js:65 msgid "Permission Denied: Please contact super admin for help with verifying this organization." msgstr "Permission Denied: Please contact super admin for help with verifying this organization." -#: src/auth/check-domain-permission.js:22 -#: src/auth/check-domain-permission.js:51 -#: src/auth/check-domain-permission.js:61 -msgid "Permission check error. Unable to request domain information." -msgstr "Permission check error. Unable to request domain information." - -#: src/auth/check-user-is-admin-for-user.js:20 -#: src/auth/check-user-is-admin-for-user.js:30 -#: src/auth/check-user-is-admin-for-user.js:63 -#: src/auth/check-user-is-admin-for-user.js:73 +#: src/auth/checks/check-user-is-admin-for-user.js:20 +#: src/auth/checks/check-user-is-admin-for-user.js:30 +#: src/auth/checks/check-user-is-admin-for-user.js:63 +#: src/auth/checks/check-user-is-admin-for-user.js:73 msgid "Permission error, not an admin for this user." msgstr "Permission error, not an admin for this user." -#: src/user/mutations/close-account.js:143 +#: src/user/mutations/close-account.js:148 msgid "Permission error: Unable to close other user's account." msgstr "Permission error: Unable to close other user's account." -#: src/auth/super-admin-required.js:11 -#: src/organization/queries/get-all-organization-domain-statuses.js:33 +#: src/auth/guards/super-admin-required.js:11 msgid "Permissions error. You do not have sufficient permissions to access this data." msgstr "Permissions error. You do not have sufficient permissions to access this data." -#: src/user/mutations/remove-phone-number.js:75 +#: src/user/mutations/remove-phone-number.js:65 msgid "Phone number has been successfully removed." msgstr "Phone number has been successfully removed." -#: src/user/mutations/set-phone-number.js:116 +#: src/user/mutations/set-phone-number.js:118 msgid "Phone number has been successfully set, you will receive a verification text message shortly." msgstr "Phone number has been successfully set, you will receive a verification text message shortly." #: src/domain/mutations/create-domain.js:118 -#: src/domain/mutations/update-domain.js:120 -msgid "Please provide a comment when adding an outside domain." -msgstr "Please provide a comment when adding an outside domain." +#: src/domain/mutations/update-domain.js:124 +#~ msgid "Please provide a comment when adding an outside domain." +#~ msgstr "Please provide a comment when adding an outside domain." -#: src/user/mutations/update-user-profile.js:184 +#: src/user/mutations/update-user-profile.js:188 msgid "Profile successfully updated." msgstr "Profile successfully updated." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:95 +#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:146 +#~ msgid "Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records." + +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:121 +#~ msgid "Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:171 +#~ msgid "Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records." + +#: src/web-scan/loaders/load-https-connections-by-domain-id.js:179 +#~ msgid "Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records." + +#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:165 +#~ msgid "Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records." + +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:203 +#~ msgid "Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:107 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:193 msgid "Requesting `{amount}` records on the `Affiliation` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `Affiliation` connection exceeds the `{argSet}` limit of 100 records." @@ -369,7 +582,7 @@ msgid "Requesting `{amount}` records on the `DmarcSummaries` connection exceeds msgstr "Requesting `{amount}` records on the `DmarcSummaries` connection exceeds the `{argSet}` limit of 100 records." #: src/domain/loaders/load-domain-connections-by-organizations-id.js:164 -#: src/domain/loaders/load-domain-connections-by-user-id.js:178 +#: src/domain/loaders/load-domain-connections-by-user-id.js:191 msgid "Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records." @@ -391,7 +604,7 @@ msgid "Requesting `{amount}` records on the `Log` connection exceeds the `{argSe msgstr "Requesting `{amount}` records on the `Log` connection exceeds the `{argSet}` limit of 100 records." #: src/organization/loaders/load-organization-connections-by-domain-id.js:189 -#: src/organization/loaders/load-organization-connections-by-user-id.js:187 +#: src/organization/loaders/load-organization-connections-by-user-id.js:199 msgid "Requesting `{amount}` records on the `Organization` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `Organization` connection exceeds the `{argSet}` limit of 100 records." @@ -399,7 +612,7 @@ msgstr "Requesting `{amount}` records on the `Organization` connection exceeds t msgid "Requesting `{amount}` records on the `SpfFailureTable` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `SpfFailureTable` connection exceeds the `{argSet}` limit of 100 records." -#: src/user/loaders/load-user-connections-by-user-id.js:139 +#: src/user/loaders/load-user-connections-by-user-id.js:131 msgid "Requesting `{amount}` records on the `User` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `User` connection exceeds the `{argSet}` limit of 100 records." @@ -413,31 +626,8 @@ msgstr "Requesting `{amount}` records on the `VerifiedDomain` connection exceeds msgid "Requesting `{amount}` records on the `VerifiedOrganization` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `VerifiedOrganization` connection exceeds the `{argSet}` limit of 100 records." -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:121 -#~ msgid "Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records." - -#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:146 -#~ msgid "Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records." - -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:171 -#~ msgid "Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records." - -#: src/web-scan/loaders/load-https-connections-by-domain-id.js:179 -#~ msgid "Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records." - -#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:165 -#~ msgid "Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records." - -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:203 -#~ msgid "Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records." - -#: src/domain/mutations/add-organizations-domains.js:338 +#. placeholder {0}: org.slug +#: src/domain/mutations/add-organizations-domains.js:334 msgid "Successfully added {domainCount} domain(s) to {0}." msgstr "Successfully added {domainCount} domain(s) to {0}." @@ -445,64 +635,75 @@ msgstr "Successfully added {domainCount} domain(s) to {0}." #~ msgid "Successfully added {domainCount} domains to {0}." #~ msgstr "Successfully added {domainCount} domains to {0}." -#: src/organization/mutations/archive-organization.js:188 +#. placeholder {0}: organization.slug +#: src/organization/mutations/archive-organization.js:100 msgid "Successfully archived organization: {0}." msgstr "Successfully archived organization: {0}." -#: src/user/mutations/close-account.js:92 -#: src/user/mutations/close-account.js:221 +#: src/user/mutations/close-account.js:96 +#: src/user/mutations/close-account.js:230 msgid "Successfully closed account." msgstr "Successfully closed account." -#: src/domain/mutations/request-scan.js:166 +#: src/domain/mutations/request-scan.js:175 msgid "Successfully dispatched one time scan." msgstr "Successfully dispatched one time scan." -#: src/domain/mutations/request-discovery.js:132 +#: src/domain/mutations/request-discovery.js:134 msgid "Successfully dispatched subdomain discovery scan." msgstr "Successfully dispatched subdomain discovery scan." #: src/user/mutations/verify-account.js:97 -msgid "Successfully email verified account, and set TFA send method to email." -msgstr "Successfully email verified account, and set TFA send method to email." +#~ msgid "Successfully email verified account, and set TFA send method to email." +#~ msgstr "Successfully email verified account, and set TFA send method to email." -#: src/affiliation/mutations/invite-user-to-org.js:283 +#: src/user/mutations/verify-account.js:143 +msgid "Successfully email verified account." +msgstr "Successfully email verified account." + +#: src/affiliation/mutations/invite-user-to-org.js:282 msgid "Successfully invited user to organization, and sent notification email." msgstr "Successfully invited user to organization, and sent notification email." -#: src/affiliation/mutations/leave-organization.js:84 +#. placeholder {0}: org.slug +#: src/affiliation/mutations/leave-organization.js:86 msgid "Successfully left organization: {0}" msgstr "Successfully left organization: {0}" -#: src/domain/mutations/unfavourite-domain.js:124 -msgid "Successfully removed domain: {0} from favourites." -msgstr "Successfully removed domain: {0} from favourites." +#. placeholder {0}: org.slug +#: src/domain/mutations/remove-organizations-domains.js:477 +msgid "Successfully removed {domainCount} domain(s) from {0}." +msgstr "Successfully removed {domainCount} domain(s) from {0}." -#: src/domain/mutations/remove-domain.js:359 +#: src/domain/mutations/remove-organizations-domains.js:530 +#~ msgid "Successfully removed {domainCount} domains from {0}." +#~ msgstr "Successfully removed {domainCount} domains from {0}." + +#. placeholder {0}: domain.domain +#. placeholder {1}: org.slug +#: src/domain/mutations/remove-domain.js:373 msgid "Successfully removed domain: {0} from {1}." msgstr "Successfully removed domain: {0} from {1}." -#: src/organization/mutations/remove-organization.js:401 +#. placeholder {0}: domain.domain +#: src/domain/mutations/unfavourite-domain.js:126 +msgid "Successfully removed domain: {0} from favourites." +msgstr "Successfully removed domain: {0} from favourites." + +#. placeholder {0}: organization.slug +#: src/organization/mutations/remove-organization.js:107 msgid "Successfully removed organization: {0}." msgstr "Successfully removed organization: {0}." -#: src/affiliation/mutations/remove-user-from-org.js:196 +#: src/affiliation/mutations/remove-user-from-org.js:201 msgid "Successfully removed user from organization." msgstr "Successfully removed user from organization." -#: src/domain/mutations/remove-organizations-domains.js:459 -msgid "Successfully removed {domainCount} domain(s) from {0}." -msgstr "Successfully removed {domainCount} domain(s) from {0}." - -#: src/domain/mutations/remove-organizations-domains.js:530 -#~ msgid "Successfully removed {domainCount} domains from {0}." -#~ msgstr "Successfully removed {domainCount} domains from {0}." - -#: src/affiliation/mutations/request-org-affiliation.js:237 +#: src/affiliation/mutations/request-org-affiliation.js:231 msgid "Successfully requested invite to organization, and sent notification email." msgstr "Successfully requested invite to organization, and sent notification email." -#: src/affiliation/mutations/invite-user-to-org.js:174 +#: src/affiliation/mutations/invite-user-to-org.js:170 msgid "Successfully sent invitation to service, and organization email." msgstr "Successfully sent invitation to service, and organization email." @@ -510,26 +711,50 @@ msgstr "Successfully sent invitation to service, and organization email." msgid "Successfully signed out." msgstr "Successfully signed out." -#: src/affiliation/mutations/transfer-org-ownership.js:185 +#. placeholder {0}: org.slug +#. placeholder {1}: requestedUser.userName +#: src/affiliation/mutations/transfer-org-ownership.js:188 msgid "Successfully transferred org: {0} ownership to user: {1}" msgstr "Successfully transferred org: {0} ownership to user: {1}" -#: src/organization/mutations/verify-organization.js:135 +#. placeholder {0}: org.slug +#. placeholder {1}: tags.join(', ') +#: src/domain/mutations/update-domains-by-domain-ids.js:179 +#: src/domain/mutations/update-domains-by-filters.js:310 +msgid "Successfully updated {domainCount} domain(s) in {0} with {1}." +msgstr "Successfully updated {domainCount} domain(s) in {0} with {1}." + +#. placeholder {0}: currentOrg.slug +#: src/organization/mutations/verify-organization.js:90 msgid "Successfully verified organization: {0}." msgstr "Successfully verified organization: {0}." -#: src/user/mutations/verify-phone-number.js:89 +#: src/user/mutations/verify-phone-number.js:91 msgid "Successfully verified phone number, and set TFA send method to text." msgstr "Successfully verified phone number, and set TFA send method to text." -#: src/user/mutations/authenticate.js:64 +#: src/tags/mutations/update-tag.js:113 +#~ msgid "Tag label already in use, please choose another and try again." +#~ msgstr "Tag label already in use, please choose another and try again." + +#: src/tags/mutations/create-tag.js:94 +#: src/tags/mutations/create-tag.js:148 +#: src/tags/mutations/update-tag.js:161 +msgid "Tag label already in use. Please try again with a different label." +msgstr "Tag label already in use. Please try again with a different label." + +#: src/user/mutations/authenticate.js:66 msgid "Token value incorrect, please sign in again." msgstr "Token value incorrect, please sign in again." -#: src/user/mutations/sign-in.js:76 +#: src/user/mutations/sign-in.js:78 msgid "Too many failed login attempts, please reset your password, and try again." msgstr "Too many failed login attempts, please reset your password, and try again." +#: src/user/mutations/complete-tour.js:73 +msgid "Tour completion confirmed successfully" +msgstr "Tour completion confirmed successfully" + #: src/user/mutations/verify-phone-number.js:51 msgid "Two factor code is incorrect. Please try again." msgstr "Two factor code is incorrect. Please try again." @@ -538,96 +763,116 @@ msgstr "Two factor code is incorrect. Please try again." msgid "Two factor code length is incorrect. Please try again." msgstr "Two factor code length is incorrect. Please try again." -#: src/affiliation/mutations/leave-organization.js:70 -#: src/affiliation/mutations/leave-organization.js:77 +#: src/affiliation/mutations/leave-organization.js:71 +#: src/affiliation/mutations/leave-organization.js:79 msgid "Unable leave organization. Please try again." msgstr "Unable leave organization. Please try again." -#: src/domain/mutations/add-organizations-domains.js:111 +#: src/domain/mutations/add-organizations-domains.js:108 msgid "Unable to add domains in unknown organization." msgstr "Unable to add domains in unknown organization." -#: src/organization/mutations/archive-organization.js:101 -#: src/organization/mutations/archive-organization.js:111 -#: src/organization/mutations/archive-organization.js:129 -#: src/organization/mutations/archive-organization.js:146 -#: src/organization/mutations/archive-organization.js:155 +#: src/organization/data-source.js:179 +#: src/organization/data-source.js:194 +#: src/organization/data-source.js:209 +#: src/organization/data-source.js:217 msgid "Unable to archive organization. Please try again." msgstr "Unable to archive organization. Please try again." -#: src/organization/mutations/archive-organization.js:54 +#: src/organization/mutations/archive-organization.js:52 msgid "Unable to archive unknown organization." msgstr "Unable to archive unknown organization." -#: src/user/mutations/authenticate.js:77 -#: src/user/mutations/authenticate.js:115 -#: src/user/mutations/authenticate.js:139 -#: src/user/mutations/authenticate.js:147 +#: src/user/mutations/authenticate.js:79 +#: src/user/mutations/authenticate.js:121 +#: src/user/mutations/authenticate.js:146 +#: src/user/mutations/authenticate.js:155 msgid "Unable to authenticate. Please try again." msgstr "Unable to authenticate. Please try again." -#: src/auth/check-permission.js:26 -#: src/auth/check-permission.js:64 -#: src/auth/check-super-admin.js:20 -#: src/auth/check-super-admin.js:30 +#: src/auth/checks/check-permission.js:26 +#: src/auth/checks/check-permission.js:64 +#: src/auth/checks/check-super-admin.js:20 +#: src/auth/checks/check-super-admin.js:30 +#: src/auth/loaders/load-permission-by-org-id.js:27 +#: src/auth/loaders/load-permission-by-org-id.js:73 msgid "Unable to check permission. Please try again." msgstr "Unable to check permission. Please try again." -#: src/user/mutations/close-account.js:155 +#: src/user/mutations/close-account.js:160 msgid "Unable to close account of an undefined user." msgstr "Unable to close account of an undefined user." -#: src/user/mutations/close-account.js:48 -#: src/user/mutations/close-account.js:63 -#: src/user/mutations/close-account.js:70 -#: src/user/mutations/close-account.js:177 -#: src/user/mutations/close-account.js:192 +#: src/user/mutations/close-account.js:49 +#: src/user/mutations/close-account.js:65 +#: src/user/mutations/close-account.js:73 +#: src/user/mutations/close-account.js:183 #: src/user/mutations/close-account.js:199 +#: src/user/mutations/close-account.js:207 msgid "Unable to close account. Please try again." msgstr "Unable to close account. Please try again." -#: src/domain/mutations/create-domain.js:131 +#: src/user/mutations/complete-tour.js:39 +#: src/user/mutations/complete-tour.js:64 +msgid "Unable to confirm completion of the tour. Please try again." +msgstr "Unable to confirm completion of the tour. Please try again." + +#: src/domain/mutations/create-domain.js:121 msgid "Unable to create domain in unknown organization." msgstr "Unable to create domain in unknown organization." -#: src/domain/mutations/create-domain.js:197 +#: src/domain/mutations/create-domain.js:216 msgid "Unable to create domain, organization has already claimed it." msgstr "Unable to create domain, organization has already claimed it." -#: src/domain/mutations/create-domain.js:179 -#: src/domain/mutations/create-domain.js:187 -#: src/domain/mutations/create-domain.js:218 -#: src/domain/mutations/create-domain.js:226 -#: src/domain/mutations/create-domain.js:245 -#: src/domain/mutations/create-domain.js:266 -#: src/domain/mutations/create-domain.js:276 -#: src/domain/mutations/create-domain.js:294 -#: src/domain/mutations/create-domain.js:302 +#: src/domain/mutations/create-domain.js:198 +#: src/domain/mutations/create-domain.js:206 +#: src/domain/mutations/create-domain.js:238 +#: src/domain/mutations/create-domain.js:247 +#: src/domain/mutations/create-domain.js:267 +#: src/domain/mutations/create-domain.js:275 msgid "Unable to create domain. Please try again." msgstr "Unable to create domain. Please try again." -#: src/domain/mutations/add-organizations-domains.js:283 +#: src/domain/mutations/add-organizations-domains.js:277 msgid "Unable to create domains. Please try again." msgstr "Unable to create domains. Please try again." -#: src/organization/mutations/create-organization.js:182 -#: src/organization/mutations/create-organization.js:202 -#: src/organization/mutations/create-organization.js:211 +#: src/organization/data-source.js:58 +#: src/organization/data-source.js:76 +#: src/organization/data-source.js:84 msgid "Unable to create organization. Please try again." msgstr "Unable to create organization. Please try again." -#: src/domain/mutations/request-discovery.js:85 +#: src/tags/mutations/create-tag.js:119 +msgid "Unable to create tag in unknown organization." +msgstr "Unable to create tag in unknown organization." + +#: src/tags/mutations/create-tag.js:108 +msgid "Unable to create tag, tagId already in use." +msgstr "Unable to create tag, tagId already in use." + +#: src/tags/data-source.js:58 +#: src/tags/data-source.js:65 +msgid "Unable to create tag. Please try again." +msgstr "Unable to create tag. Please try again." + +#: src/domain/mutations/request-discovery.js:86 msgid "Unable to discover domains for unknown organization." msgstr "Unable to discover domains for unknown organization." +#: src/user/mutations/dismiss-message.js:39 +#: src/user/mutations/dismiss-message.js:66 +msgid "Unable to dismiss message. Please try again." +msgstr "Unable to dismiss message. Please try again." + #: src/domain/mutations/request-scan.js:95 #: src/domain/mutations/request-scan.js:109 #: src/domain/mutations/request-scan.js:123 #~ msgid "Unable to dispatch one time scan. Please try again." #~ msgstr "Unable to dispatch one time scan. Please try again." -#: src/organization/objects/organization.js:194 -#: src/organization/objects/organization.js:204 +#: src/organization/objects/organization.js:265 msgid "Unable to export organization. Please try again." msgstr "Unable to export organization. Please try again." @@ -637,8 +882,8 @@ msgstr "Unable to favourite domain, user has already favourited it." #: src/domain/mutations/favourite-domain.js:66 #: src/domain/mutations/favourite-domain.js:74 -#: src/domain/mutations/favourite-domain.js:102 -#: src/domain/mutations/favourite-domain.js:109 +#: src/domain/mutations/favourite-domain.js:103 +#: src/domain/mutations/favourite-domain.js:111 #: src/domain/mutations/unfavourite-domain.js:68 #: src/domain/mutations/unfavourite-domain.js:76 msgid "Unable to favourite domain. Please try again." @@ -688,6 +933,11 @@ msgstr "Unable to find DMARC summary data. Please try again." msgid "Unable to find DNS scan(s). Please try again." msgstr "Unable to find DNS scan(s). Please try again." +#: src/guidance-tag/loaders/load-guidance-tags.js:26 +#: src/guidance-tag/loaders/load-guidance-tags.js:36 +msgid "Unable to find guidance tag(s). Please try again." +msgstr "Unable to find guidance tag(s). Please try again." + #: src/guidance-tag/loaders/load-https-guidance-tags.js:33 #: src/guidance-tag/loaders/load-https-guidance-tags.js:47 msgid "Unable to find HTTPS guidance tag(s). Please try again." @@ -733,26 +983,33 @@ msgstr "Unable to find user affiliation(s). Please try again." msgid "Unable to find verified organization(s). Please try again." msgstr "Unable to find verified organization(s). Please try again." -#: src/affiliation/mutations/invite-user-to-org.js:119 -#: src/affiliation/mutations/invite-user-to-org.js:129 -#: src/affiliation/mutations/invite-user-to-org.js:194 +#: src/domain/mutations/ignore-cve.js:64 +#: src/domain/mutations/ignore-cve.js:101 +#: src/domain/mutations/ignore-cve.js:123 +#: src/domain/mutations/ignore-cve.js:138 +#: src/domain/mutations/ignore-cve.js:149 +msgid "Unable to ignore CVE. Please try again." +msgstr "Unable to ignore CVE. Please try again." + +#: src/affiliation/mutations/invite-user-to-org.js:124 +#: src/affiliation/mutations/invite-user-to-org.js:190 msgid "Unable to invite user to organization. Please try again." msgstr "Unable to invite user to organization. Please try again." -#: src/affiliation/mutations/invite-user-to-org.js:206 +#: src/affiliation/mutations/invite-user-to-org.js:202 msgid "Unable to invite user to organization. User is already affiliated with organization." msgstr "Unable to invite user to organization. User is already affiliated with organization." -#: src/affiliation/mutations/invite-user-to-org.js:82 +#: src/affiliation/mutations/invite-user-to-org.js:84 msgid "Unable to invite user to unknown organization." msgstr "Unable to invite user to unknown organization." -#: src/affiliation/mutations/invite-user-to-org.js:235 -#: src/affiliation/mutations/invite-user-to-org.js:255 +#: src/affiliation/mutations/invite-user-to-org.js:232 +#: src/affiliation/mutations/invite-user-to-org.js:253 msgid "Unable to invite user. Please try again." msgstr "Unable to invite user. Please try again." -#: src/affiliation/mutations/invite-user-to-org.js:68 +#: src/affiliation/mutations/invite-user-to-org.js:70 msgid "Unable to invite yourself to an org." msgstr "Unable to invite yourself to an org." @@ -765,11 +1022,34 @@ msgstr "Unable to invite yourself to an org." msgid "Unable to leave undefined organization." msgstr "Unable to leave undefined organization." +#: src/additional-findings/loaders/load-additional-findings-by-domain-id.js:24 +#: src/additional-findings/loaders/load-additional-findings-by-domain-id.js:34 +msgid "Unable to load additional findings. Please try again." +msgstr "Unable to load additional findings. Please try again." + +#: src/auth/checks/check-user-belongs-to-org.js:20 +msgid "Unable to load affiliation information. Please try again." +msgstr "Unable to load affiliation information. Please try again." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:303 +#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:449 +msgid "Unable to load affiliation(s). Please try again." +msgstr "Unable to load affiliation(s). Please try again." + #: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:254 #: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:266 msgid "Unable to load Aggregate guidance tag(s). Please try again." msgstr "Unable to load Aggregate guidance tag(s). Please try again." +#: src/organization/loaders/load-all-organization-domain-statuses.js:57 +#~ msgid "Unable to load all organization domain statuses. Please try again." +#~ msgstr "Unable to load all organization domain statuses. Please try again." + +#: src/summaries/loaders/load-chart-summaries-by-period.js:50 +#: src/summaries/loaders/load-chart-summaries-by-period.js:60 +msgid "Unable to load chart summary data. Please try again." +msgstr "Unable to load chart summary data. Please try again." + #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:141 #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:153 @@ -792,8 +1072,8 @@ msgstr "Unable to load DKIM guidance tag(s). Please try again." #~ msgstr "Unable to load DKIM scan(s). Please try again." #: src/summaries/queries/dkim-summary.js:12 -msgid "Unable to load DKIM summary. Please try again." -msgstr "Unable to load DKIM summary. Please try again." +#~ msgid "Unable to load DKIM summary. Please try again." +#~ msgstr "Unable to load DKIM summary. Please try again." #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:141 @@ -806,17 +1086,17 @@ msgstr "Unable to load DMARC failure data. Please try again." msgid "Unable to load DMARC guidance tag(s). Please try again." msgstr "Unable to load DMARC guidance tag(s). Please try again." -#: src/summaries/queries/dmarc-phase-summary.js:14 -msgid "Unable to load DMARC phase summary. Please try again." -msgstr "Unable to load DMARC phase summary. Please try again." +#: src/summaries/queries/dmarc-phase-summary.js:12 +#~ msgid "Unable to load DMARC phase summary. Please try again." +#~ msgstr "Unable to load DMARC phase summary. Please try again." #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:319 #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:331 #~ msgid "Unable to load DMARC scan(s). Please try again." #~ msgstr "Unable to load DMARC scan(s). Please try again." -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:446 -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:456 +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:449 +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:459 #: src/dmarc-summaries/loaders/load-dmarc-sum-edge-by-domain-id-period.js:20 #: src/dmarc-summaries/loaders/load-dmarc-sum-edge-by-domain-id-period.js:32 #: src/dmarc-summaries/loaders/load-yearly-dmarc-sum-edges.js:20 @@ -825,16 +1105,44 @@ msgid "Unable to load DMARC summary data. Please try again." msgstr "Unable to load DMARC summary data. Please try again." #: src/summaries/queries/dmarc-summary.js:12 -msgid "Unable to load DMARC summary. Please try again." -msgstr "Unable to load DMARC summary. Please try again." +#~ msgid "Unable to load DMARC summary. Please try again." +#~ msgstr "Unable to load DMARC summary. Please try again." #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:154 #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:164 -#: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:156 -#: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:166 msgid "Unable to load DNS scan(s). Please try again." msgstr "Unable to load DNS scan(s). Please try again." +#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:18 +#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:28 +msgid "Unable to load domain selector(s). Please try again." +msgstr "Unable to load domain selector(s). Please try again." + +#: src/domain/loaders/load-domain-by-domain.js:19 +#: src/domain/loaders/load-domain-by-domain.js:31 +#: src/domain/loaders/load-domain-by-key.js:19 +#: src/domain/loaders/load-domain-by-key.js:31 +msgid "Unable to load domain. Please try again." +msgstr "Unable to load domain. Please try again." + +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:402 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:412 +#: src/domain/loaders/load-domain-connections-by-user-id.js:469 +#: src/user/loaders/load-my-tracker-by-user-id.js:33 +msgid "Unable to load domain(s). Please try again." +msgstr "Unable to load domain(s). Please try again." + +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:13 +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:140 +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:152 +msgid "Unable to load full pass data. Please try again." +msgstr "Unable to load full pass data. Please try again." + +#: src/guidance-tag/loaders/load-guidance-tags-connections.js:54 +#: src/guidance-tag/loaders/load-guidance-tags-connections.js:64 +msgid "Unable to load guidance tag(s). Please try again." +msgstr "Unable to load guidance tag(s). Please try again." + #: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:260 #: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:272 msgid "Unable to load HTTPS guidance tag(s). Please try again." @@ -847,8 +1155,62 @@ msgstr "Unable to load HTTPS guidance tag(s). Please try again." #~ msgstr "Unable to load HTTPS scan(s). Please try again." #: src/summaries/queries/https-summary.js:13 -msgid "Unable to load HTTPS summary. Please try again." -msgstr "Unable to load HTTPS summary. Please try again." +#~ msgid "Unable to load HTTPS summary. Please try again." +#~ msgstr "Unable to load HTTPS summary. Please try again." + +#: src/audit-logs/loaders/load-audit-log-by-key.js:19 +#: src/audit-logs/loaders/load-audit-log-by-key.js:31 +msgid "Unable to load log. Please try again." +msgstr "Unable to load log. Please try again." + +#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:318 +msgid "Unable to load log(s). Please try again." +msgstr "Unable to load log(s). Please try again." + +#: src/summaries/queries/mail-summary.js:12 +#~ msgid "Unable to load mail summary. Please try again." +#~ msgstr "Unable to load mail summary. Please try again." + +#: src/additional-findings/loaders/load-top-25-reports.js:29 +#: src/organization/loaders/load-all-organization-domain-statuses.js:164 +#: src/organization/loaders/load-organization-domain-statuses.js:172 +msgid "Unable to load organization domain statuses. Please try again." +msgstr "Unable to load organization domain statuses. Please try again." + +#: src/organization/loaders/load-organization-names-by-id.js:19 +#: src/organization/loaders/load-organization-names-by-id.js:29 +msgid "Unable to load organization names. Please try again." +msgstr "Unable to load organization names. Please try again." + +#: src/organization/loaders/load-organization-summaries-by-period.js:56 +#: src/organization/loaders/load-organization-summaries-by-period.js:66 +msgid "Unable to load organization summary data. Please try again." +msgstr "Unable to load organization summary data. Please try again." + +#: src/organization/data-source.js:117 +#: src/organization/data-source.js:124 +#: src/organization/data-source.js:144 +#: src/organization/data-source.js:152 +msgid "Unable to load organization. Please try again." +msgstr "Unable to load organization. Please try again." + +#: src/organization/loaders/load-organization-by-key.js:31 +#: src/organization/loaders/load-organization-by-key.js:41 +#: src/organization/loaders/load-organization-by-slug.js:34 +#: src/organization/loaders/load-organization-by-slug.js:45 +#: src/organization/loaders/load-organization-connections-by-domain-id.js:508 +#: src/organization/loaders/load-organization-connections-by-domain-id.js:518 +#: src/organization/loaders/load-organization-connections-by-user-id.js:544 +#: src/organization/loaders/load-organization-connections-by-user-id.js:554 +msgid "Unable to load organization(s). Please try again." +msgstr "Unable to load organization(s). Please try again." + +#: src/auth/checks/check-org-owner.js:19 +#: src/auth/checks/check-org-owner.js:27 +#: src/auth/loaders/load-org-owner-by-org-id.js:23 +#: src/auth/loaders/load-org-owner-by-org-id.js:33 +msgid "Unable to load owner information. Please try again." +msgstr "Unable to load owner information. Please try again." #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:140 @@ -867,8 +1229,8 @@ msgstr "Unable to load SPF guidance tag(s). Please try again." #~ msgstr "Unable to load SPF scan(s). Please try again." #: src/summaries/queries/spf-summary.js:12 -msgid "Unable to load SPF summary. Please try again." -msgstr "Unable to load SPF summary. Please try again." +#~ msgid "Unable to load SPF summary. Please try again." +#~ msgstr "Unable to load SPF summary. Please try again." #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:260 #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:272 @@ -881,105 +1243,32 @@ msgstr "Unable to load SSL guidance tag(s). Please try again." #~ msgstr "Unable to load SSL scan(s). Please try again." #: src/summaries/queries/ssl-summary.js:12 -msgid "Unable to load SSL summary. Please try again." -msgstr "Unable to load SSL summary. Please try again." - -#: src/auth/check-user-belongs-to-org.js:20 -msgid "Unable to load affiliation information. Please try again." -msgstr "Unable to load affiliation information. Please try again." - -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:259 -#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:449 -msgid "Unable to load affiliation(s). Please try again." -msgstr "Unable to load affiliation(s). Please try again." - -#: src/organization/loaders/load-all-organization-domain-statuses.js:57 -msgid "Unable to load all organization domain statuses. Please try again." -msgstr "Unable to load all organization domain statuses. Please try again." - -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:122 -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:132 -msgid "Unable to load chart summary data. Please try again." -msgstr "Unable to load chart summary data. Please try again." - -#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:18 -#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:28 -msgid "Unable to load domain selector(s). Please try again." -msgstr "Unable to load domain selector(s). Please try again." - -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:554 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:564 -#: src/domain/loaders/load-domain-connections-by-user-id.js:452 -#: src/domain/loaders/load-domain-tags-by-org-id.js:27 -#: src/user/loaders/load-my-tracker-by-user-id.js:33 -msgid "Unable to load domain(s). Please try again." -msgstr "Unable to load domain(s). Please try again." - -#: src/domain/loaders/load-domain-by-domain.js:19 -#: src/domain/loaders/load-domain-by-domain.js:31 -#: src/domain/loaders/load-domain-by-key.js:19 -#: src/domain/loaders/load-domain-by-key.js:31 -msgid "Unable to load domain. Please try again." -msgstr "Unable to load domain. Please try again." - -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:13 -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:140 -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:152 -msgid "Unable to load full pass data. Please try again." -msgstr "Unable to load full pass data. Please try again." - -#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:318 -msgid "Unable to load log(s). Please try again." -msgstr "Unable to load log(s). Please try again." - -#: src/audit-logs/loaders/load-audit-log-by-key.js:19 -#: src/audit-logs/loaders/load-audit-log-by-key.js:31 -msgid "Unable to load log. Please try again." -msgstr "Unable to load log. Please try again." - -#: src/summaries/queries/mail-summary.js:12 -msgid "Unable to load mail summary. Please try again." -msgstr "Unable to load mail summary. Please try again." - -#: src/organization/loaders/load-organization-domain-statuses.js:118 -msgid "Unable to load organization domain statuses. Please try again." -msgstr "Unable to load organization domain statuses. Please try again." - -#: src/organization/loaders/load-organization-summaries-by-period.js:108 -#: src/organization/loaders/load-organization-summaries-by-period.js:118 -msgid "Unable to load organization summary data. Please try again." -msgstr "Unable to load organization summary data. Please try again." - -#: src/organization/loaders/load-organization-by-key.js:33 -#: src/organization/loaders/load-organization-by-key.js:47 -#: src/organization/loaders/load-organization-by-slug.js:36 -#: src/organization/loaders/load-organization-by-slug.js:51 -#: src/organization/loaders/load-organization-connections-by-domain-id.js:499 -#: src/organization/loaders/load-organization-connections-by-domain-id.js:509 -#: src/organization/loaders/load-organization-connections-by-user-id.js:505 -#: src/organization/loaders/load-organization-connections-by-user-id.js:515 -msgid "Unable to load organization(s). Please try again." -msgstr "Unable to load organization(s). Please try again." - -#: src/auth/check-org-owner.js:19 -#: src/auth/check-org-owner.js:27 -msgid "Unable to load owner information. Please try again." -msgstr "Unable to load owner information. Please try again." +#~ msgid "Unable to load SSL summary. Please try again." +#~ msgstr "Unable to load SSL summary. Please try again." #: src/summaries/loaders/load-chart-summary-by-key.js:17 #: src/summaries/loaders/load-chart-summary-by-key.js:25 -msgid "Unable to load summary. Please try again." -msgstr "Unable to load summary. Please try again." +#~ msgid "Unable to load summary. Please try again." +#~ msgstr "Unable to load summary. Please try again." + +#: src/tags/loaders/load-all-tags.js:36 +#: src/tags/loaders/load-all-tags.js:44 +#: src/tags/loaders/load-tag-by-tag-id.js:25 +#: src/tags/loaders/load-tag-by-tag-id.js:35 +#: src/tags/loaders/load-tags-by-org.js:36 +#: src/tags/loaders/load-tags-by-org.js:44 +msgid "Unable to load tag(s). Please try again." +msgstr "Unable to load tag(s). Please try again." #: src/domain/loaders/load-domain-tags-by-org-id.js:37 -msgid "Unable to load tags(s). Please try again." -msgstr "Unable to load tags(s). Please try again." +#~ msgid "Unable to load tags(s). Please try again." +#~ msgstr "Unable to load tags(s). Please try again." #: src/user/loaders/load-user-by-key.js:19 #: src/user/loaders/load-user-by-key.js:31 #: src/user/loaders/load-user-by-username.js:19 #: src/user/loaders/load-user-by-username.js:31 -#: src/user/loaders/load-user-connections-by-user-id.js:345 +#: src/user/loaders/load-user-connections-by-user-id.js:346 msgid "Unable to load user(s). Please try again." msgstr "Unable to load user(s). Please try again." @@ -1001,9 +1290,13 @@ msgstr "Unable to load verified domain(s). Please try again." msgid "Unable to load verified organization(s). Please try again." msgstr "Unable to load verified organization(s). Please try again." +#: src/dmarc-summaries/loaders/load-all-verified-rua-domains.js:27 +msgid "Unable to load verified rua domains. Please try again." +msgstr "Unable to load verified rua domains. Please try again." + #: src/summaries/queries/web-connections-summary.js:12 -msgid "Unable to load web connections summary. Please try again." -msgstr "Unable to load web connections summary. Please try again." +#~ msgid "Unable to load web connections summary. Please try again." +#~ msgstr "Unable to load web connections summary. Please try again." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:169 #: src/web-scan/loaders/load-web-connections-by-domain-id.js:179 @@ -1014,15 +1307,15 @@ msgid "Unable to load web scan(s). Please try again." msgstr "Unable to load web scan(s). Please try again." #: src/summaries/queries/web-summary.js:13 -msgid "Unable to load web summary. Please try again." -msgstr "Unable to load web summary. Please try again." +#~ msgid "Unable to load web summary. Please try again." +#~ msgstr "Unable to load web summary. Please try again." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:249 +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:293 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:437 msgid "Unable to query affiliation(s). Please try again." msgstr "Unable to query affiliation(s). Please try again." -#: src/domain/loaders/load-domain-connections-by-user-id.js:442 +#: src/domain/loaders/load-domain-connections-by-user-id.js:459 #: src/user/loaders/load-my-tracker-by-user-id.js:23 msgid "Unable to query domain(s). Please try again." msgstr "Unable to query domain(s). Please try again." @@ -1031,80 +1324,78 @@ msgstr "Unable to query domain(s). Please try again." msgid "Unable to query log(s). Please try again." msgstr "Unable to query log(s). Please try again." -#: src/user/loaders/load-user-connections-by-user-id.js:335 +#: src/user/loaders/load-user-connections-by-user-id.js:336 msgid "Unable to query user(s). Please try again." msgstr "Unable to query user(s). Please try again." -#: src/user/mutations/refresh-tokens.js:49 -#: src/user/mutations/refresh-tokens.js:63 -#: src/user/mutations/refresh-tokens.js:78 -#: src/user/mutations/refresh-tokens.js:93 -#: src/user/mutations/refresh-tokens.js:105 -#: src/user/mutations/refresh-tokens.js:136 -#: src/user/mutations/refresh-tokens.js:145 +#: src/user/mutations/refresh-tokens.js:47 +#: src/user/mutations/refresh-tokens.js:59 +#: src/user/mutations/refresh-tokens.js:72 +#: src/user/mutations/refresh-tokens.js:85 +#: src/user/mutations/refresh-tokens.js:95 +#: src/user/mutations/refresh-tokens.js:123 +#: src/user/mutations/refresh-tokens.js:131 msgid "Unable to refresh tokens, please sign in." msgstr "Unable to refresh tokens, please sign in." -#: src/affiliation/mutations/remove-user-from-org.js:104 +#: src/affiliation/mutations/remove-user-from-org.js:106 msgid "Unable to remove a user that already does not belong to this organization." msgstr "Unable to remove a user that already does not belong to this organization." -#: src/domain/mutations/remove-domain.js:80 +#: src/domain/mutations/remove-domain.js:82 msgid "Unable to remove domain from unknown organization." msgstr "Unable to remove domain from unknown organization." -#: src/domain/mutations/remove-domain.js:136 +#: src/domain/mutations/remove-domain.js:138 msgid "Unable to remove domain. Domain is not part of organization." msgstr "Unable to remove domain. Domain is not part of organization." -#: src/domain/mutations/remove-domain.js:123 -#: src/domain/mutations/remove-domain.js:152 -#: src/domain/mutations/remove-domain.js:185 -#: src/domain/mutations/remove-domain.js:204 -#: src/domain/mutations/remove-domain.js:230 -#: src/domain/mutations/remove-domain.js:248 -#: src/domain/mutations/remove-domain.js:265 -#: src/domain/mutations/remove-domain.js:281 -#: src/domain/mutations/remove-domain.js:298 -#: src/domain/mutations/remove-domain.js:321 -#: src/domain/mutations/remove-domain.js:332 +#: src/domain/mutations/remove-domain.js:125 +#: src/domain/mutations/remove-domain.js:154 +#: src/domain/mutations/remove-domain.js:188 +#: src/domain/mutations/remove-domain.js:208 +#: src/domain/mutations/remove-domain.js:237 +#: src/domain/mutations/remove-domain.js:256 +#: src/domain/mutations/remove-domain.js:274 +#: src/domain/mutations/remove-domain.js:291 +#: src/domain/mutations/remove-domain.js:309 +#: src/domain/mutations/remove-domain.js:333 +#: src/domain/mutations/remove-domain.js:345 msgid "Unable to remove domain. Please try again." msgstr "Unable to remove domain. Please try again." -#: src/domain/mutations/remove-organizations-domains.js:90 +#: src/domain/mutations/remove-organizations-domains.js:91 msgid "Unable to remove domains from unknown organization." msgstr "Unable to remove domains from unknown organization." -#: src/organization/mutations/remove-organization.js:101 -#: src/organization/mutations/remove-organization.js:111 -#: src/organization/mutations/remove-organization.js:141 -#: src/organization/mutations/remove-organization.js:156 -#: src/organization/mutations/remove-organization.js:186 -#: src/organization/mutations/remove-organization.js:196 -#: src/organization/mutations/remove-organization.js:220 -#: src/organization/mutations/remove-organization.js:238 -#: src/organization/mutations/remove-organization.js:255 -#: src/organization/mutations/remove-organization.js:271 -#: src/organization/mutations/remove-organization.js:303 -#: src/organization/mutations/remove-organization.js:367 -#: src/organization/mutations/remove-organization.js:376 +#: src/organization/data-source.js:278 +#: src/organization/data-source.js:307 +#: src/organization/data-source.js:321 +#: src/organization/data-source.js:350 +#: src/organization/data-source.js:374 +#: src/organization/data-source.js:390 +#: src/organization/data-source.js:405 +#: src/organization/data-source.js:419 +#: src/organization/data-source.js:448 +#: src/organization/data-source.js:490 +#: src/organization/data-source.js:498 msgid "Unable to remove organization. Please try again." msgstr "Unable to remove organization. Please try again." -#: src/user/mutations/remove-phone-number.js:57 -#: src/user/mutations/remove-phone-number.js:68 +#: src/user/mutations/remove-phone-number.js:51 +#: src/user/mutations/remove-phone-number.js:59 msgid "Unable to remove phone number. Please try again." msgstr "Unable to remove phone number. Please try again." -#: src/domain/mutations/remove-domain.js:65 +#: src/domain/mutations/remove-domain.js:67 msgid "Unable to remove unknown domain." msgstr "Unable to remove unknown domain." -#: src/organization/mutations/remove-organization.js:54 +#: src/organization/mutations/remove-organization.js:52 msgid "Unable to remove unknown organization." msgstr "Unable to remove unknown organization." -#: src/affiliation/mutations/remove-user-from-org.js:77 +#: src/affiliation/mutations/remove-user-from-org.js:79 msgid "Unable to remove unknown user from organization." msgstr "Unable to remove unknown user from organization." @@ -1112,71 +1403,70 @@ msgstr "Unable to remove unknown user from organization." #~ msgid "Unable to remove user from organization." #~ msgstr "Unable to remove user from organization." -#: src/affiliation/mutations/remove-user-from-org.js:94 -#: src/affiliation/mutations/remove-user-from-org.js:115 -#: src/affiliation/mutations/remove-user-from-org.js:161 -#: src/affiliation/mutations/remove-user-from-org.js:170 +#: src/affiliation/mutations/remove-user-from-org.js:96 +#: src/affiliation/mutations/remove-user-from-org.js:117 +#: src/affiliation/mutations/remove-user-from-org.js:164 +#: src/affiliation/mutations/remove-user-from-org.js:174 msgid "Unable to remove user from this organization. Please try again." msgstr "Unable to remove user from this organization. Please try again." -#: src/affiliation/mutations/remove-user-from-org.js:61 +#: src/affiliation/mutations/remove-user-from-org.js:63 msgid "Unable to remove user from unknown organization." msgstr "Unable to remove user from unknown organization." -#: src/domain/mutations/request-scan.js:117 +#: src/domain/mutations/request-scan.js:120 msgid "Unable to request a one time scan on a domain that already has a pending scan." msgstr "Unable to request a one time scan on a domain that already has a pending scan." -#: src/domain/mutations/request-scan.js:52 +#: src/domain/mutations/request-scan.js:55 msgid "Unable to request a one time scan on an unknown domain." msgstr "Unable to request a one time scan on an unknown domain." -#: src/domain/mutations/request-scan.js:125 +#: src/domain/mutations/request-scan.js:128 msgid "Unable to request a one time scan. Please try again." msgstr "Unable to request a one time scan. Please try again." -#: src/domain/mutations/request-discovery.js:62 +#: src/domain/mutations/request-discovery.js:63 msgid "Unable to request a subdomain discovery scan on an invalid domain." msgstr "Unable to request a subdomain discovery scan on an invalid domain." -#: src/domain/mutations/request-discovery.js:72 +#: src/domain/mutations/request-discovery.js:73 msgid "Unable to request a subdomain discovery scan on an unknown domain." msgstr "Unable to request a subdomain discovery scan on an unknown domain." -#: src/affiliation/mutations/request-org-affiliation.js:97 +#: src/affiliation/mutations/request-org-affiliation.js:98 msgid "Unable to request invite to organization with which you are already affiliated." msgstr "Unable to request invite to organization with which you are already affiliated." -#: src/affiliation/mutations/request-org-affiliation.js:87 +#: src/affiliation/mutations/request-org-affiliation.js:88 msgid "Unable to request invite to organization with which you have already requested to join." msgstr "Unable to request invite to organization with which you have already requested to join." -#: src/affiliation/mutations/request-org-affiliation.js:58 +#: src/affiliation/mutations/request-org-affiliation.js:59 msgid "Unable to request invite to unknown organization." msgstr "Unable to request invite to unknown organization." -#: src/affiliation/mutations/request-org-affiliation.js:74 -#: src/affiliation/mutations/request-org-affiliation.js:122 -#: src/affiliation/mutations/request-org-affiliation.js:138 -#: src/affiliation/mutations/request-org-affiliation.js:148 -#: src/affiliation/mutations/request-org-affiliation.js:174 -#: src/affiliation/mutations/request-org-affiliation.js:183 -#: src/affiliation/mutations/request-org-affiliation.js:205 +#: src/affiliation/mutations/request-org-affiliation.js:75 +#: src/affiliation/mutations/request-org-affiliation.js:124 +#: src/affiliation/mutations/request-org-affiliation.js:141 +#: src/affiliation/mutations/request-org-affiliation.js:152 +#: src/affiliation/mutations/request-org-affiliation.js:167 +#: src/affiliation/mutations/request-org-affiliation.js:198 msgid "Unable to request invite. Please try again." msgstr "Unable to request invite. Please try again." #: src/user/mutations/reset-password.js:86 -msgid "Unable to reset password. Please request a new email." -msgstr "Unable to reset password. Please request a new email." +#~ msgid "Unable to reset password. Please request a new email." +#~ msgstr "Unable to reset password. Please request a new email." -#: src/user/mutations/reset-password.js:74 -#: src/user/mutations/reset-password.js:134 -#: src/user/mutations/reset-password.js:141 +#: src/user/mutations/reset-password.js:76 +#: src/user/mutations/reset-password.js:125 +#: src/user/mutations/reset-password.js:133 msgid "Unable to reset password. Please try again." msgstr "Unable to reset password. Please try again." -#: src/domain/objects/domain.js:290 -#: src/domain/objects/domain.js:325 +#: src/domain/objects/domain.js:274 +#: src/domain/objects/domain.js:309 msgid "Unable to retrieve DMARC report information for: {domain}" msgstr "Unable to retrieve DMARC report information for: {domain}" @@ -1206,45 +1496,62 @@ msgstr "Unable to send org invite request email. Please try again." msgid "Unable to send password reset email. Please try again." msgstr "Unable to send password reset email. Please try again." +#: src/notify/notify-send-role-change-email.js:21 +#~ msgid "Unable to send role update email. Please try again." +#~ msgstr "Unable to send role update email. Please try again." + #: src/notify/notify-send-tfa-text-msg.js:30 -msgid "Unable to send two factor authentication message. Please try again." -msgstr "Unable to send two factor authentication message. Please try again." +#~ msgid "Unable to send two factor authentication message. Please try again." +#~ msgstr "Unable to send two factor authentication message. Please try again." + +#: src/notify/notify-send-updated-username-email.js:18 +#: src/user/mutations/verify-account.js:102 +msgid "Unable to send updated username email. Please try again." +msgstr "Unable to send updated username email. Please try again." -#: src/notify/notify-send-verification-email.js:30 +#: src/notify/notify-send-verification-email.js:18 msgid "Unable to send verification email. Please try again." msgstr "Unable to send verification email. Please try again." -#: src/user/mutations/set-phone-number.js:96 -#: src/user/mutations/set-phone-number.js:103 +#: src/user/mutations/set-phone-number.js:97 +#: src/user/mutations/set-phone-number.js:105 msgid "Unable to set phone number, please try again." msgstr "Unable to set phone number, please try again." -#: src/user/mutations/sign-in.js:95 -#: src/user/mutations/sign-in.js:128 -#: src/user/mutations/sign-in.js:135 -#: src/user/mutations/sign-in.js:179 -#: src/user/mutations/sign-in.js:186 -#: src/user/mutations/sign-in.js:246 -#: src/user/mutations/sign-in.js:253 +#: src/user/mutations/sign-in.js:98 +#: src/user/mutations/sign-in.js:132 +#: src/user/mutations/sign-in.js:140 +#: src/user/mutations/sign-in.js:198 +#: src/user/mutations/sign-in.js:206 +#: src/user/mutations/sign-in.js:270 +#: src/user/mutations/sign-in.js:278 msgid "Unable to sign in, please try again." msgstr "Unable to sign in, please try again." -#: src/user/mutations/sign-up.js:183 -#: src/user/mutations/sign-up.js:193 +#: src/user/mutations/sign-up.js:197 +#: src/user/mutations/sign-up.js:208 msgid "Unable to sign up, please contact org admin for a new invite." msgstr "Unable to sign up, please contact org admin for a new invite." -#: src/user/mutations/sign-up.js:156 -#: src/user/mutations/sign-up.js:164 -#: src/user/mutations/sign-up.js:213 -#: src/user/mutations/sign-up.js:221 +#: src/user/mutations/sign-up.js:168 +#: src/user/mutations/sign-up.js:177 +#: src/user/mutations/sign-up.js:229 +#: src/user/mutations/sign-up.js:238 msgid "Unable to sign up. Please try again." msgstr "Unable to sign up. Please try again." +#: src/domain/mutations/unignore-cve.js:64 +#: src/domain/mutations/unignore-cve.js:101 +#: src/domain/mutations/unignore-cve.js:123 +#: src/domain/mutations/unignore-cve.js:138 +#: src/domain/mutations/unignore-cve.js:149 +msgid "Unable to stop ignoring CVE. Please try again." +msgstr "Unable to stop ignoring CVE. Please try again." + #: src/affiliation/mutations/transfer-org-ownership.js:106 -#: src/affiliation/mutations/transfer-org-ownership.js:145 -#: src/affiliation/mutations/transfer-org-ownership.js:167 -#: src/affiliation/mutations/transfer-org-ownership.js:177 +#: src/affiliation/mutations/transfer-org-ownership.js:146 +#: src/affiliation/mutations/transfer-org-ownership.js:169 +#: src/affiliation/mutations/transfer-org-ownership.js:180 msgid "Unable to transfer organization ownership. Please try again." msgstr "Unable to transfer organization ownership. Please try again." @@ -1264,8 +1571,8 @@ msgstr "Unable to transfer ownership of undefined organization." msgid "Unable to transfer ownership to a user outside the org. Please invite the user and try again." msgstr "Unable to transfer ownership to a user outside the org. Please invite the user and try again." -#: src/user/mutations/verify-phone-number.js:71 -#: src/user/mutations/verify-phone-number.js:78 +#: src/user/mutations/verify-phone-number.js:72 +#: src/user/mutations/verify-phone-number.js:80 msgid "Unable to two factor authenticate. Please try again." msgstr "Unable to two factor authenticate. Please try again." @@ -1273,8 +1580,8 @@ msgstr "Unable to two factor authenticate. Please try again." msgid "Unable to unfavourite domain, domain is not favourited." msgstr "Unable to unfavourite domain, domain is not favourited." -#: src/domain/mutations/unfavourite-domain.js:110 -#: src/domain/mutations/unfavourite-domain.js:117 +#: src/domain/mutations/unfavourite-domain.js:111 +#: src/domain/mutations/unfavourite-domain.js:119 msgid "Unable to unfavourite domain. Please try again." msgstr "Unable to unfavourite domain. Please try again." @@ -1282,32 +1589,42 @@ msgstr "Unable to unfavourite domain. Please try again." msgid "Unable to unfavourite unknown domain." msgstr "Unable to unfavourite unknown domain." -#: src/domain/mutations/update-domain.js:260 +#: src/domain/mutations/unignore-cve.js:62 +#: src/domain/mutations/unignore-cve.js:98 +#: src/domain/mutations/unignore-cve.js:108 +#~ msgid "Unable to unignore CVE. Please try again." +#~ msgstr "Unable to unignore CVE. Please try again." + +#: src/domain/mutations/update-domain.js:284 msgid "Unable to update domain edge. Please try again." msgstr "Unable to update domain edge. Please try again." -#: src/domain/mutations/update-domain.js:149 +#: src/domain/mutations/update-domain.js:148 msgid "Unable to update domain in an unknown org." msgstr "Unable to update domain in an unknown org." -#: src/domain/mutations/update-domain.js:190 +#: src/domain/mutations/update-domain.js:189 msgid "Unable to update domain that does not belong to the given organization." msgstr "Unable to update domain that does not belong to the given organization." -#: src/domain/mutations/update-domain.js:180 -#: src/domain/mutations/update-domain.js:219 -#: src/domain/mutations/update-domain.js:301 -#: src/domain/mutations/update-domain.js:326 -#: src/domain/mutations/update-domain.js:348 -#: src/domain/mutations/update-domain.js:360 +#: src/domain/mutations/update-domain.js:179 +#: src/domain/mutations/update-domain.js:241 +#: src/domain/mutations/update-domain.js:295 msgid "Unable to update domain. Please try again." msgstr "Unable to update domain. Please try again." -#: src/organization/mutations/update-organization.js:164 -#: src/organization/mutations/update-organization.js:190 -#: src/organization/mutations/update-organization.js:198 -#: src/organization/mutations/update-organization.js:243 -#: src/organization/mutations/update-organization.js:250 +#: src/domain/mutations/update-domains-by-domain-ids.js:68 +#: src/domain/mutations/update-domains-by-filters.js:76 +msgid "Unable to update domains in unknown organization." +msgstr "Unable to update domains in unknown organization." + +#: src/domain/mutations/update-domains-by-filters.js:229 +#: src/domain/mutations/update-domains-by-filters.js:237 +#: src/domain/mutations/update-domains-by-filters.js:244 +msgid "Unable to update domains. Please try again." +msgstr "Unable to update domains. Please try again." + +#: src/organization/data-source.js:101 msgid "Unable to update organization. Please try again." msgstr "Unable to update organization. Please try again." @@ -1323,54 +1640,79 @@ msgstr "Unable to update password, new passwords do not match. Please try again. msgid "Unable to update password, passwords do not match requirements. Please try again." msgstr "Unable to update password, passwords do not match requirements. Please try again." -#: src/user/mutations/update-user-password.js:94 -#: src/user/mutations/update-user-password.js:101 +#: src/user/mutations/update-user-password.js:95 +#: src/user/mutations/update-user-password.js:103 msgid "Unable to update password. Please try again." msgstr "Unable to update password. Please try again." -#: src/user/mutations/update-user-profile.js:160 -#: src/user/mutations/update-user-profile.js:167 +#: src/user/mutations/update-user-profile.js:154 +#: src/user/mutations/update-user-profile.js:162 msgid "Unable to update profile. Please try again." msgstr "Unable to update profile. Please try again." -#: src/affiliation/mutations/update-user-role.js:94 +#: src/affiliation/mutations/update-user-role.js:97 msgid "Unable to update role: organization unknown." msgstr "Unable to update role: organization unknown." -#: src/affiliation/mutations/update-user-role.js:137 +#: src/affiliation/mutations/update-user-role.js:140 msgid "Unable to update role: user does not belong to organization." msgstr "Unable to update role: user does not belong to organization." -#: src/affiliation/mutations/update-user-role.js:80 +#: src/affiliation/mutations/update-user-role.js:83 msgid "Unable to update role: user unknown." msgstr "Unable to update role: user unknown." -#: src/domain/mutations/update-domain.js:135 +#: src/tags/mutations/update-tag.js:113 +msgid "Unable to update tag in unknown organization." +msgstr "Unable to update tag in unknown organization." + +#: src/tags/mutations/update-tag.js:103 +msgid "Unable to update tag, orgId is invalid." +msgstr "Unable to update tag, orgId is invalid." + +#: src/tags/mutations/update-tag.js:127 +#~ msgid "Unable to update tag, tagId already in use." +#~ msgstr "Unable to update tag, tagId already in use." + +#: src/tags/data-source.js:30 +#: src/tags/data-source.js:36 +#: src/tags/data-source.js:92 +#: src/tags/data-source.js:101 +msgid "Unable to update tag. Please try again." +msgstr "Unable to update tag. Please try again." + +#: src/domain/mutations/update-domain.js:134 msgid "Unable to update unknown domain." msgstr "Unable to update unknown domain." -#: src/organization/mutations/update-organization.js:130 +#: src/organization/mutations/update-organization.js:137 msgid "Unable to update unknown organization." msgstr "Unable to update unknown organization." -#: src/affiliation/mutations/update-user-role.js:127 -#: src/affiliation/mutations/update-user-role.js:149 -#: src/affiliation/mutations/update-user-role.js:201 -#: src/affiliation/mutations/update-user-role.js:211 +#: src/tags/mutations/update-tag.js:87 +msgid "Unable to update unknown tag." +msgstr "Unable to update unknown tag." + +#: src/affiliation/mutations/update-user-role.js:130 +#: src/affiliation/mutations/update-user-role.js:152 +#: src/affiliation/mutations/update-user-role.js:196 +#: src/affiliation/mutations/update-user-role.js:206 +#: src/affiliation/mutations/update-user-role.js:217 msgid "Unable to update user's role. Please try again." msgstr "Unable to update user's role. Please try again." -#: src/affiliation/mutations/update-user-role.js:66 +#: src/affiliation/mutations/update-user-role.js:69 msgid "Unable to update your own role." msgstr "Unable to update your own role." -#: src/user/mutations/verify-account.js:49 +#: src/user/mutations/verify-account.js:51 #: src/user/mutations/verify-account.js:63 +#: src/user/mutations/verify-account.js:77 msgid "Unable to verify account. Please request a new email." msgstr "Unable to verify account. Please request a new email." -#: src/user/mutations/verify-account.js:83 -#: src/user/mutations/verify-account.js:90 +#: src/user/mutations/verify-account.js:128 +#: src/user/mutations/verify-account.js:136 msgid "Unable to verify account. Please try again." msgstr "Unable to verify account. Please try again." @@ -1378,18 +1720,18 @@ msgstr "Unable to verify account. Please try again." msgid "Unable to verify if user is a super admin, please try again." msgstr "Unable to verify if user is a super admin, please try again." -#: src/user/mutations/update-user-profile.js:100 -#: src/user/queries/is-user-admin.js:55 +#: src/user/mutations/update-user-profile.js:99 +#: src/user/queries/is-user-admin.js:49 msgid "Unable to verify if user is an admin, please try again." msgstr "Unable to verify if user is an admin, please try again." -#: src/organization/mutations/verify-organization.js:105 -#: src/organization/mutations/verify-organization.js:121 -#: src/organization/mutations/verify-organization.js:128 +#: src/organization/data-source.js:237 +#: src/organization/data-source.js:252 +#: src/organization/data-source.js:260 msgid "Unable to verify organization. Please try again." msgstr "Unable to verify organization. Please try again." -#: src/organization/mutations/verify-organization.js:53 +#: src/organization/mutations/verify-organization.js:50 msgid "Unable to verify unknown organization." msgstr "Unable to verify unknown organization." @@ -1397,43 +1739,52 @@ msgstr "Unable to verify unknown organization." msgid "User could not be queried." msgstr "User could not be queried." -#: src/affiliation/mutations/update-user-role.js:244 +#: src/user/mutations/sign-up.js:79 +msgid "User is trying to register for a non-production environment." +msgstr "User is trying to register for a non-production environment." + +#: src/affiliation/mutations/update-user-role.js:253 msgid "User role was updated successfully." msgstr "User role was updated successfully." -#: src/user/mutations/update-user-profile.js:81 +#: src/user/mutations/update-user-profile.js:80 +#: src/user/mutations/verify-account.js:88 msgid "Username not available, please try another." msgstr "Username not available, please try another." -#: src/auth/tfa-required.js:15 +#: src/auth/guards/tfa-required.js:15 msgid "Verification error. Please activate multi-factor authentication to access content." msgstr "Verification error. Please activate multi-factor authentication to access content." -#: src/auth/verified-required.js:15 +#: src/auth/guards/verified-required.js:15 msgid "Verification error. Please verify your account via email to access content." msgstr "Verification error. Please verify your account via email to access content." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:70 +#: src/additional-findings/loaders/load-additional-findings-by-domain-id.js:8 +msgid "You must provide a `domainId` to retrieve a domain's additional findings." +msgstr "You must provide a `domainId` to retrieve a domain's additional findings." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:82 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:161 msgid "You must provide a `first` or `last` value to properly paginate the `Affiliation` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `Affiliation` connection." -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:89 -#~ msgid "You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection." -#~ msgstr "You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection." - #: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:114 #~ msgid "You must provide a `first` or `last` value to properly paginate the `DKIM` connection." #~ msgstr "You must provide a `first` or `last` value to properly paginate the `DKIM` connection." -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:138 -#~ msgid "You must provide a `first` or `last` value to properly paginate the `DMARC` connection." -#~ msgstr "You must provide a `first` or `last` value to properly paginate the `DMARC` connection." - #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:36 msgid "You must provide a `first` or `last` value to properly paginate the `DkimFailureTable` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `DkimFailureTable` connection." +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:89 +#~ msgid "You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection." +#~ msgstr "You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:138 +#~ msgid "You must provide a `first` or `last` value to properly paginate the `DMARC` connection." +#~ msgstr "You must provide a `first` or `last` value to properly paginate the `DMARC` connection." + #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:36 msgid "You must provide a `first` or `last` value to properly paginate the `DmarcFailureTable` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `DmarcFailureTable` connection." @@ -1443,7 +1794,7 @@ msgid "You must provide a `first` or `last` value to properly paginate the `Dmar msgstr "You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection." #: src/domain/loaders/load-domain-connections-by-organizations-id.js:141 -#: src/domain/loaders/load-domain-connections-by-user-id.js:153 +#: src/domain/loaders/load-domain-connections-by-user-id.js:166 msgid "You must provide a `first` or `last` value to properly paginate the `Domain` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `Domain` connection." @@ -1469,7 +1820,7 @@ msgid "You must provide a `first` or `last` value to properly paginate the `Log` msgstr "You must provide a `first` or `last` value to properly paginate the `Log` connection." #: src/organization/loaders/load-organization-connections-by-domain-id.js:166 -#: src/organization/loaders/load-organization-connections-by-user-id.js:166 +#: src/organization/loaders/load-organization-connections-by-user-id.js:178 msgid "You must provide a `first` or `last` value to properly paginate the `Organization` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `Organization` connection." @@ -1477,15 +1828,15 @@ msgstr "You must provide a `first` or `last` value to properly paginate the `Org #~ msgid "You must provide a `first` or `last` value to properly paginate the `SPF` connection." #~ msgstr "You must provide a `first` or `last` value to properly paginate the `SPF` connection." -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:171 -#~ msgid "You must provide a `first` or `last` value to properly paginate the `SSL` connection." -#~ msgstr "You must provide a `first` or `last` value to properly paginate the `SSL` connection." - #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:36 msgid "You must provide a `first` or `last` value to properly paginate the `SpfFailureTable` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `SpfFailureTable` connection." -#: src/user/loaders/load-user-connections-by-user-id.js:107 +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:171 +#~ msgid "You must provide a `first` or `last` value to properly paginate the `SSL` connection." +#~ msgstr "You must provide a `first` or `last` value to properly paginate the `SSL` connection." + +#: src/user/loaders/load-user-connections-by-user-id.js:106 msgid "You must provide a `first` or `last` value to properly paginate the `User` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `User` connection." @@ -1504,8 +1855,8 @@ msgid "You must provide a `limit` value in the range of 1-100 to properly pagina msgstr "You must provide a `limit` value in the range of 1-100 to properly paginate the `DNS` connection." #: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:16 -msgid "You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection." -msgstr "You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection." +#~ msgid "You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection." +#~ msgstr "You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:16 msgid "You must provide a `limit` value in the range of 1-100 to properly paginate the `web` connection." @@ -1516,157 +1867,53 @@ msgid "You must provide a `limit` value to properly paginate the `DNS` connectio msgstr "You must provide a `limit` value to properly paginate the `DNS` connection." #: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:9 -msgid "You must provide a `limit` value to properly paginate the `MXRecord` connection." -msgstr "You must provide a `limit` value to properly paginate the `MXRecord` connection." +#~ msgid "You must provide a `limit` value to properly paginate the `MXRecord` connection." +#~ msgstr "You must provide a `limit` value to properly paginate the `MXRecord` connection." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:9 msgid "You must provide a `limit` value to properly paginate the `web` connection." msgstr "You must provide a `limit` value to properly paginate the `web` connection." -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:9 -msgid "You must provide a `period` value to access the `ChartSummaries` connection." -msgstr "You must provide a `period` value to access the `ChartSummaries` connection." +#: src/summaries/loaders/load-chart-summaries-by-period.js:8 +#~ msgid "You must provide a `period` value to access the `ChartSummaries` connection." +#~ msgstr "You must provide a `period` value to access the `ChartSummaries` connection." #: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:12 msgid "You must provide a `period` value to access the `DmarcSummaries` connection." msgstr "You must provide a `period` value to access the `DmarcSummaries` connection." #: src/organization/loaders/load-organization-summaries-by-period.js:10 -msgid "You must provide a `period` value to access the `OrganizationSummaries` connection." -msgstr "You must provide a `period` value to access the `OrganizationSummaries` connection." +#~ msgid "You must provide a `period` value to access the `OrganizationSummaries` connection." +#~ msgstr "You must provide a `period` value to access the `OrganizationSummaries` connection." -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:30 -msgid "You must provide a `year` value to access the `ChartSummaries` connection." -msgstr "You must provide a `year` value to access the `ChartSummaries` connection." +#: src/summaries/loaders/load-chart-summaries-by-period.js:43 +#~ msgid "You must provide a `year` value to access the `ChartSummaries` connection." +#~ msgstr "You must provide a `year` value to access the `ChartSummaries` connection." #: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:18 msgid "You must provide a `year` value to access the `DmarcSummaries` connection." msgstr "You must provide a `year` value to access the `DmarcSummaries` connection." -#: src/organization/loaders/load-organization-summaries-by-period.js:32 -msgid "You must provide a `year` value to access the `OrganizationSummaries` connection." -msgstr "You must provide a `year` value to access the `OrganizationSummaries` connection." +#: src/organization/loaders/load-organization-summaries-by-period.js:46 +#~ msgid "You must provide a `year` value to access the `OrganizationSummaries` connection." +#~ msgstr "You must provide a `year` value to access the `OrganizationSummaries` connection." #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:30 msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `DNS` connection." msgstr "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `DNS` connection." #: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:30 -msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection." -msgstr "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection." +#~ msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection." +#~ msgstr "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:30 msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `web` connection." msgstr "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `web` connection." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:109 -#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:208 -#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:131 -#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:83 -#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:83 -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:202 -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:83 -#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:83 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:178 -#: src/domain/loaders/load-domain-connections-by-user-id.js:192 -#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:132 -#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:136 -#: src/organization/loaders/load-organization-connections-by-domain-id.js:203 -#: src/organization/loaders/load-organization-connections-by-user-id.js:201 -#: src/user/loaders/load-user-connections-by-user-id.js:154 -#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:164 -#: src/verified-domains/loaders/load-verified-domain-connections.js:164 -#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:214 -#: src/verified-organizations/loaders/load-verified-organizations-connections.js:212 -msgid "`{argSet}` must be of type `number` not `{typeSet}`." -msgstr "`{argSet}` must be of type `number` not `{typeSet}`." - -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:86 -#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:182 -msgid "`{argSet}` on the `Affiliation` connection cannot be less than zero." -msgstr "`{argSet}` on the `Affiliation` connection cannot be less than zero." - -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:110 -#~ msgid "`{argSet}` on the `DKIMResults` connection cannot be less than zero." -#~ msgstr "`{argSet}` on the `DKIMResults` connection cannot be less than zero." - -#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:135 -#~ msgid "`{argSet}` on the `DKIM` connection cannot be less than zero." -#~ msgstr "`{argSet}` on the `DKIM` connection cannot be less than zero." +#: src/summaries/loaders/load-chart-summaries-by-period.js:13 +#~ msgid "You must provide both `startDate` and `endDate` values to access the `ChartSummaries` connection." +#~ msgstr "You must provide both `startDate` and `endDate` values to access the `ChartSummaries` connection." -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:160 -#~ msgid "`{argSet}` on the `DMARC` connection cannot be less than zero." -#~ msgstr "`{argSet}` on the `DMARC` connection cannot be less than zero." - -#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `DkimFailureTable` connection cannot be less than zero." -msgstr "`{argSet}` on the `DkimFailureTable` connection cannot be less than zero." - -#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero." -msgstr "`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero." - -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:179 -msgid "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." -msgstr "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." - -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:157 -#: src/domain/loaders/load-domain-connections-by-user-id.js:169 -msgid "`{argSet}` on the `Domain` connection cannot be less than zero." -msgstr "`{argSet}` on the `Domain` connection cannot be less than zero." - -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `FullPassTable` connection cannot be less than zero." -msgstr "`{argSet}` on the `FullPassTable` connection cannot be less than zero." - -#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:106 -#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:110 -msgid "`{argSet}` on the `GuidanceTag` connection cannot be less than zero." -msgstr "`{argSet}` on the `GuidanceTag` connection cannot be less than zero." - -#: src/web-scan/loaders/load-https-connections-by-domain-id.js:168 -#~ msgid "`{argSet}` on the `HTTPS` connection cannot be less than zero." -#~ msgstr "`{argSet}` on the `HTTPS` connection cannot be less than zero." - -#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:112 -msgid "`{argSet}` on the `Log` connection cannot be less than zero." -msgstr "`{argSet}` on the `Log` connection cannot be less than zero." - -#: src/organization/loaders/load-organization-connections-by-domain-id.js:182 -#: src/organization/loaders/load-organization-connections-by-user-id.js:180 -msgid "`{argSet}` on the `Organization` connection cannot be less than zero." -msgstr "`{argSet}` on the `Organization` connection cannot be less than zero." - -#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:154 -#~ msgid "`{argSet}` on the `SPF` connection cannot be less than zero." -#~ msgstr "`{argSet}` on the `SPF` connection cannot be less than zero." - -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:192 -#~ msgid "`{argSet}` on the `SSL` connection cannot be less than zero." -#~ msgstr "`{argSet}` on the `SSL` connection cannot be less than zero." - -#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `SpfFailureTable` connection cannot be less than zero." -msgstr "`{argSet}` on the `SpfFailureTable` connection cannot be less than zero." - -#: src/user/loaders/load-user-connections-by-user-id.js:128 -msgid "`{argSet}` on the `User` connection cannot be less than zero." -msgstr "`{argSet}` on the `User` connection cannot be less than zero." - -#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:138 -#: src/verified-domains/loaders/load-verified-domain-connections.js:138 -msgid "`{argSet}` on the `VerifiedDomain` connection cannot be less than zero." -msgstr "`{argSet}` on the `VerifiedDomain` connection cannot be less than zero." - -#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:188 -#: src/verified-organizations/loaders/load-verified-organizations-connections.js:186 -msgid "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." -msgstr "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." +#: src/organization/loaders/load-organization-summaries-by-period.js:13 +#~ msgid "You must provide both `startDate` and `endDate` values to access the `OrganizationSummaries` connection." +#~ msgstr "You must provide both `startDate` and `endDate` values to access the `OrganizationSummaries` connection." diff --git a/api/src/locale/fr/messages.js b/api/src/locale/fr/messages.js index 940486f56..1ef81295f 100644 --- a/api/src/locale/fr/messages.js +++ b/api/src/locale/fr/messages.js @@ -1 +1 @@ -/*eslint-disable*/module.exports={messages:{"Authentication error. Please sign in.":"Erreur d'authentification. Veuillez vous connecter.","Cannot query affiliations on organization without admin permission or higher.":"Impossible d'interroger les affiliations sur l'organisation sans l'autorisation de l'administrateur ou plus.","Cannot query audit logs on organization without admin permission or higher.":"Impossible d'interroger les journaux d'audit sur l'organisation sans l'autorisation d'administrateur ou plus.","Cannot query dns scan results without permission.":"Impossible d'interroger les résultats de l'analyse DNS sans autorisation.","Cannot query domain selectors without permission.":"Impossible d'interroger les sélecteurs de domaine sans autorisation.","Cannot query web scan results without permission.":"Impossible d'interroger les résultats de l'analyse web sans autorisation.","Email already in use.":"Courriel déjà utilisé.","Error while requesting scan. Please try again.":"Erreur lors de la demande d'analyse. Veuillez réessayer.","If an account with this username is found, a password reset link will be found in your inbox.":"Si un compte avec ce nom d'utilisateur est trouvé, un lien de réinitialisation du mot de passe se trouvera dans votre boîte de réception.","If an account with this username is found, an email verification link will be found in your inbox.":"Si un compte avec ce nom d'utilisateur est trouvé, un lien de vérification par e-mail sera trouvé dans votre boîte de réception.","Incorrect TFA code. Please sign in again.":"Code TFA incorrect. Veuillez vous reconnecter.","Incorrect token value. Please request a new email.":"La valeur du jeton est incorrecte. Veuillez demander un nouvel e-mail.","Incorrect username or password. Please try again.":"Le nom d'utilisateur ou le mot de passe est incorrect. Veuillez réessayer.","Invalid token, please sign in.":"Jeton invalide, veuillez vous connecter.","New passwords do not match.":"Les nouveaux mots de passe ne correspondent pas.","No organization with the provided slug could be found.":"Aucune organisation avec le slug fourni n'a pu être trouvée.","No verified domain with the provided domain could be found.":"Aucun domaine vérifié avec le domaine fourni n'a pu être trouvé.","No verified organization with the provided slug could be found.":"Aucune organisation vérifiée avec le slug fourni n'a pu être trouvée.","Organization has already been verified.":"L'organisation a déjà été vérifiée.","Organization name already in use, please choose another and try again.":"Le nom de l'organisation est déjà utilisé, veuillez en choisir un autre et réessayer.","Organization name already in use. Please try again with a different name.":"Le nom de l'organisation est déjà utilisé. Veuillez réessayer avec un nom différent.","Ownership check error. Unable to request domain information.":"Erreur de vérification de la propriété. Impossible de demander des informations sur le domaine.","Passing both `first` and `last` to paginate the `Affiliation` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `Affiliation` n'est pas supporté.","Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `DKIMResults` n'est pas supporté.","Passing both `first` and `last` to paginate the `DKIM` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `DKIMResults` n'est pas supporté.","Passing both `first` and `last` to paginate the `DMARC` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `DMARC` n'est pas supporté.","Passing both `first` and `last` to paginate the `DkimFailureTable` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `DkimFailureTable` n'est pas supporté.","Passing both `first` and `last` to paginate the `DmarcFailureTable` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `DmarcFailureTable` n'est pas supporté.","Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `DmarcSummaries` n'est pas supporté.","Passing both `first` and `last` to paginate the `Domain` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `Domain` n'est pas supporté.","Passing both `first` and `last` to paginate the `FullPassTable` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `FullPassTable` n'est pas supporté.","Passing both `first` and `last` to paginate the `GuidanceTag` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `GuidanceTag` n'est pas supporté.","Passing both `first` and `last` to paginate the `HTTPS` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `HTTPS` n'est pas supporté.","Passing both `first` and `last` to paginate the `Log` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `Log` n'est pas supporté.","Passing both `first` and `last` to paginate the `Organization` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `Organization` n'est pas supporté.","Passing both `first` and `last` to paginate the `SPF` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `SPF` n'est pas supporté.","Passing both `first` and `last` to paginate the `SSL` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `SSL` n'est pas supporté.","Passing both `first` and `last` to paginate the `SpfFailureTable` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `SpfFailureTable` n'est pas supporté.","Passing both `first` and `last` to paginate the `User` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `User` n'est pas supporté.","Passing both `first` and `last` to paginate the `VerifiedDomain` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `VerifiedDomain` n'est pas supporté.","Passing both `first` and `last` to paginate the `VerifiedOrganization` connection is not supported.":"Passer à la fois `first` et `last` pour paginer la connexion `VerifiedOrganization` n'est pas supporté.","Password does not meet requirements.":"Le mot de passe ne répond pas aux exigences.","Password was successfully reset.":"Le mot de passe a été réinitialisé avec succès.","Password was successfully updated.":"Le mot de passe a été mis à jour avec succès.","Passwords do not match.":"Les mots de passe ne correspondent pas.","Permission Denied: Could not retrieve specified organization.":"Permission refusée : Impossible de récupérer l'organisation spécifiée.","Permission Denied: Multi-factor authentication is required for admin accounts":"Permission refusée : L'authentification multifactorielle est requise pour les comptes admin.","Permission Denied: Please contact org owner to transfer ownership.":"Permission refusée : Veuillez contacter le propriétaire de l'org pour transférer la propriété.","Permission Denied: Please contact organization admin for help with archiving domains.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur l'archivage des domaines.","Permission Denied: Please contact organization admin for help with removing domain.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide afin de supprimer le domaine.","Permission Denied: Please contact organization admin for help with removing domains.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des domaines.","Permission Denied: Please contact organization admin for help with removing organization.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide afin de supprimer l'organisation.","Permission Denied: Please contact organization admin for help with removing users.":"Autorisation refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.","Permission Denied: Please contact organization admin for help with updating organization.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.","Permission Denied: Please contact organization admin for help with updating user roles.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la mise à jour des rôles des utilisateurs.","Permission Denied: Please contact organization admin for help with user invitations.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant les invitations d'utilisateurs.","Permission Denied: Please contact organization admin for help with user role changes.":"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur les changements de rôle des utilisateurs.","Permission Denied: Please contact organization user for help with creating domain.":"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la création du domaine.","Permission Denied: Please contact organization user for help with creating domains.":"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la création de domaines.","Permission Denied: Please contact organization user for help with retrieving this domain.":"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide pour récupérer ce domaine.","Permission Denied: Please contact organization user for help with scanning this domain.":"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur l'analyse de ce domaine.","Permission Denied: Please contact organization user for help with updating this domain.":"Autorisation refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine.","Permission Denied: Please contact super admin for help with archiving organization.":"Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'organisation de l'archivage.","Permission Denied: Please contact super admin for help with removing domain.":"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine.","Permission Denied: Please contact super admin for help with removing organization.":"Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à supprimer l'organisation.","Permission Denied: Please contact super admin for help with scanning this domain.":"Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'analyse de ce domaine.","Permission Denied: Please contact super admin for help with verifying this organization.":"Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à vérifier cette organisation.","Permission check error. Unable to request domain information.":"Erreur de vérification des permissions. Impossible de demander des informations sur le domaine.","Permission error, not an admin for this user.":"Erreur de permission, pas d'administrateur pour cet utilisateur.","Permission error: Unable to close other user's account.":"Erreur de permission: Impossible de fermer le compte d'un autre utilisateur.","Permissions error. You do not have sufficient permissions to access this data.":"Erreur de permissions. Vous n'avez pas les autorisations suffisantes pour accéder à ces données.","Phone number has been successfully removed.":"Le numéro de téléphone a été supprimé avec succès.","Phone number has been successfully set, you will receive a verification text message shortly.":"Le numéro de téléphone a été configuré avec succès, vous recevrez bientôt un message de vérification.","Please provide a comment when adding an outside domain.":"Veuillez fournir un commentaire lorsque vous ajoutez un domaine externe.","Profile successfully updated.":"Le profil a été mis à jour avec succès.","Requesting `{amount}` records on the `Affiliation` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `Affiliation` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `DkimFailureTable` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `DkimFailureTable` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `DmarcFailureTable` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `DkimFailureTable` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `DmarcSummaries` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `DmarcSummaries` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `Domain` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `FullPassTable` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `FullPassTable` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `GuidanceTag` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `GuidanceTag` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `Log` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `Log` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `Organization` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `Organization` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `SpfFailureTable` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `SpfFailureTable` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `User` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `User` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `VerifiedDomain` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `VerifiedDomain` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting `{amount}` records on the `VerifiedOrganization` connection exceeds the `{argSet}` limit of 100 records.":["La demande d'enregistrements `",["amount"],"` sur la connexion `VerifiedOrganization` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records.":["La demande de ",["amount"]," enregistrements sur la connexion `DKIMResults` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records.":["La demande de ",["amount"]," enregistrements sur la connexion `DKIM` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records.":["La demande de ",["amount"]," enregistrements sur la connexion `DMARC` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records.":["La demande de ",["amount"]," enregistrements sur la connexion `HTTPS` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records.":["La demande de ",["amount"]," enregistrements sur la connexion `SPF` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records.":["La demande de ",["amount"]," enregistrements sur la connexion `SSL` dépasse la limite `",["argSet"],"` de 100 enregistrements."],"Successfully added {domainCount} domain(s) to {0}.":["Ajouté avec succès le(s) domaine(s) ",["domainCount"]," à ",["0"],"."],"Successfully added {domainCount} domains to {0}.":["Ajouté avec succès les domaines ",["domainCount"]," à ",["0"],"."],"Successfully archived organization: {0}.":["Organisation archivée avec succès : ",["0"],"."],"Successfully closed account.":"Le compte a été fermé avec succès.","Successfully dispatched one time scan.":"Un seul balayage a été effectué avec succès.","Successfully dispatched subdomain discovery scan.":"L'analyse de découverte du sous-domaine a été effectuée avec succès.","Successfully email verified account, and set TFA send method to email.":"Réussir à envoyer un email au compte vérifié, et définir la méthode d'envoi de la TFA sur email.","Successfully invited user to organization, and sent notification email.":"L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé.","Successfully left organization: {0}":["L'organisation a été quittée avec succès: ",["0"]],"Successfully removed domain: {0} from favourites.":["A réussi à supprimer le domaine : ",["0"]," des favoris."],"Successfully removed domain: {0} from {1}.":["A réussi à supprimer le domaine : ",["0"]," de ",["1"],"."],"Successfully removed organization: {0}.":["A réussi à supprimer l'organisation : ",["0"],"."],"Successfully removed user from organization.":"L'utilisateur a été retiré de l'organisation avec succès.","Successfully removed {domainCount} domain(s) from {0}.":["Supprimé avec succès le(s) domaine(s) ",["domainCount"]," de ",["0"],"."],"Successfully removed {domainCount} domains from {0}.":["Suppression réussie des domaines ",["domainCount"]," de ",["0"],"."],"Successfully requested invite to organization, and sent notification email.":"La demande d'invitation à l'organisation a été effectuée avec succès et un courriel de notification a été envoyé.","Successfully sent invitation to service, and organization email.":"Envoi réussi de l'invitation au service, et de l'email de l'organisation.","Successfully signed out.":"J'ai réussi à me déconnecter.","Successfully transferred org: {0} ownership to user: {1}":["A réussi à transférer la propriété de org: ",["0"]," à l'utilisateur: ",["1"]],"Successfully verified organization: {0}.":"Envoi réussi de l'invitation au service, et de l'email de l'organisation.","Successfully verified phone number, and set TFA send method to text.":"Le numéro de téléphone a été vérifié avec succès, et la méthode d'envoi de la TFA a été réglée sur le texte.","Token value incorrect, please sign in again.":"La valeur du jeton est incorrecte, veuillez vous connecter à nouveau.","Too many failed login attempts, please reset your password, and try again.":"Trop de tentatives de connexion ont échoué, veuillez réinitialiser votre mot de passe et réessayer.","Two factor code is incorrect. Please try again.":"Le code à deux facteurs est incorrect. Veuillez réessayer.","Two factor code length is incorrect. Please try again.":"La longueur du code à deux facteurs est incorrecte. Veuillez réessayer.","Unable leave organization. Please try again.":"Impossible de quitter l'organisation. Veuillez réessayer.","Unable to add domains in unknown organization.":"Impossible d'ajouter des domaines dans une organisation inconnue.","Unable to archive organization. Please try again.":"Impossible d'archiver l'organisation. Veuillez réessayer.","Unable to archive unknown organization.":"Impossible d'archiver une organisation inconnue.","Unable to authenticate. Please try again.":"Impossible de s'authentifier. Veuillez réessayer.","Unable to check permission. Please try again.":"Impossible de vérifier l'autorisation. Veuillez réessayer.","Unable to close account of an undefined user.":"Impossible de fermer le compte d'un utilisateur non défini.","Unable to close account. Please try again.":"Impossible de fermer le compte. Veuillez réessayer.","Unable to create domain in unknown organization.":"Impossible de créer un domaine dans une organisation inconnue.","Unable to create domain, organization has already claimed it.":"Impossible de créer le domaine, l'organisation l'a déjà réclamé.","Unable to create domain. Please try again.":"Impossible de créer un domaine. Veuillez réessayer.","Unable to create domains. Please try again.":"Impossible de créer des domaines. Veuillez réessayer.","Unable to create organization. Please try again.":"Impossible de créer une organisation. Veuillez réessayer.","Unable to discover domains for unknown organization.":"Impossible de découvrir les domaines d'une organisation inconnue.","Unable to dispatch one time scan. Please try again.":"Impossible d'envoyer un scan unique. Veuillez réessayer.","Unable to export organization. Please try again.":"Impossible d'exporter l'organisation. Veuillez réessayer.","Unable to favourite domain, user has already favourited it.":"Impossible de favoriser le domaine, l'utilisateur l'a déjà favorisé.","Unable to favourite domain. Please try again.":"Impossible d'accéder au domaine favori. Veuillez réessayer.","Unable to favourite unknown domain.":"Impossible de favoriser le domaine inconnu.","Unable to find Aggregate guidance tag(s). Please try again.":"Impossible de trouver le(s) tag(s) d'orientation des agrégats. Veuillez réessayer.","Unable to find DKIM guidance tag(s). Please try again.":"Impossible de trouver le(s) tag(s) d'orientation DKIM. Veuillez réessayer.","Unable to find DKIM result(s). Please try again.":"Impossible de trouver le(s) résultat(s) DKIM. Veuillez réessayer.","Unable to find DKIM scan(s). Please try again.":"Impossible de trouver le(s) scan(s) DKIM. Veuillez réessayer.","Unable to find DMARC guidance tag(s). Please try again.":"Impossible de trouver le(s) tag(s) d'orientation DMARC. Veuillez réessayer.","Unable to find DMARC scan(s). Please try again.":"Impossible de trouver le(s) scan(s) DMARC. Veuillez réessayer.","Unable to find DMARC summary data. Please try again.":"Impossible de trouver les données de synthèse DMARC. Veuillez réessayer.","Unable to find DNS scan(s). Please try again.":"Impossible de trouver le(s) scan(s) DNS. Veuillez réessayer.","Unable to find HTTPS guidance tag(s). Please try again.":"Impossible de trouver la ou les balises d'orientation HTTPS. Veuillez réessayer.","Unable to find HTTPS scan(s). Please try again.":"Impossible de trouver le(s) scan(s) HTTPS. Veuillez réessayer.","Unable to find SPF guidance tag(s). Please try again.":"Impossible de trouver le(s) tag(s) d'orientation SPF. Veuillez réessayer.","Unable to find SPF scan(s). Please try again.":"Impossible de trouver le(s) scan(s) SPF. Veuillez réessayer.","Unable to find SSL guidance tag(s). Please try again.":"Impossible de trouver le(s) tag(s) d'orientation SSL. Veuillez réessayer.","Unable to find SSL scan(s). Please try again.":"Impossible de trouver le(s) scan(s) SSL. Veuillez réessayer.","Unable to find the requested domain.":"Impossible de trouver le domaine demandé.","Unable to find user affiliation(s). Please try again.":"Impossible de trouver l'affiliation de l'utilisateur (s). Veuillez réessayer.","Unable to find verified organization(s). Please try again.":"Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.","Unable to invite user to organization. Please try again.":"Impossible d'inviter un utilisateur dans une organisation. Veuillez réessayer.","Unable to invite user to organization. User is already affiliated with organization.":"Impossible d'inviter un utilisateur dans une organisation. L'utilisateur est déjà affilié à l'organisation.","Unable to invite user to unknown organization.":"Impossible d'inviter un utilisateur à une organisation inconnue.","Unable to invite user. Please try again.":"Impossible d'inviter un utilisateur. Veuillez réessayer.","Unable to invite yourself to an org.":"Impossible de s'inviter à un org.","Unable to leave organization. Please try again.":"Impossible de quitter l'organisation. Veuillez réessayer.","Unable to leave undefined organization.":"Impossible de quitter une organisation non définie.","Unable to load Aggregate guidance tag(s). Please try again.":"Impossible de charger le(s) tag(s) d'orientation des agrégats. Veuillez réessayer.","Unable to load DKIM failure data. Please try again.":"Impossible de charger les données d'échec DKIM. Veuillez réessayer.","Unable to load DKIM guidance tag(s). Please try again.":"Impossible de charger le(s) tag(s) d'orientation DKIM. Veuillez réessayer.","Unable to load DKIM result(s). Please try again.":"Impossible de charger le(s) résultat(s) DKIM. Veuillez réessayer.","Unable to load DKIM scan(s). Please try again.":"Impossible de charger le(s) scan(s) DKIM. Veuillez réessayer.","Unable to load DKIM summary. Please try again.":"Impossible de charger le résumé DKIM. Veuillez réessayer.","Unable to load DMARC failure data. Please try again.":"Impossible de charger les données d'échec DMARC. Veuillez réessayer.","Unable to load DMARC guidance tag(s). Please try again.":"Impossible de charger le(s) tag(s) d'orientation DMARC. Veuillez réessayer.","Unable to load DMARC phase summary. Please try again.":"Impossible de charger le résumé DMARC. Veuillez réessayer.","Unable to load DMARC scan(s). Please try again.":"Impossible de charger le(s) scan(s) DMARC. Veuillez réessayer.","Unable to load DMARC summary data. Please try again.":"Impossible de charger les données de synthèse DMARC. Veuillez réessayer.","Unable to load DMARC summary. Please try again.":"Impossible de charger le résumé DMARC. Veuillez réessayer.","Unable to load DNS scan(s). Please try again.":"Impossible de charger le(s) scan(s) DNS. Veuillez réessayer.","Unable to load HTTPS guidance tag(s). Please try again.":"Impossible de charger la ou les balises d'orientation HTTPS. Veuillez réessayer.","Unable to load HTTPS scan(s). Please try again.":"Impossible de charger le(s) scan(s) HTTPS. Veuillez réessayer.","Unable to load HTTPS summary. Please try again.":"Impossible de charger le résumé HTTPS. Veuillez réessayer.","Unable to load SPF failure data. Please try again.":"Impossible de charger les données d'échec SPF. Veuillez réessayer.","Unable to load SPF guidance tag(s). Please try again.":"Impossible de charger le(s) tag(s) d'orientation SPF. Veuillez réessayer.","Unable to load SPF scan(s). Please try again.":"Impossible de charger le(s) scan(s) SPF. Veuillez réessayer.","Unable to load SPF summary. Please try again.":"Impossible de charger le résumé SPF. Veuillez réessayer.","Unable to load SSL guidance tag(s). Please try again.":"Impossible de charger le(s) tag(s) d'orientation SSL. Veuillez réessayer.","Unable to load SSL scan(s). Please try again.":"Impossible de charger le(s) scan(s) SSL. Veuillez réessayer.","Unable to load SSL summary. Please try again.":"Impossible de charger le résumé SSL. Veuillez réessayer.","Unable to load affiliation information. Please try again.":"Impossible de charger les informations d'affiliation. Veuillez réessayer.","Unable to load affiliation(s). Please try again.":"Impossible de charger l'affiliation (s). Veuillez réessayer.","Unable to load all organization domain statuses. Please try again.":"Impossible de charger tous les statuts de domaine d'organisation. Veuillez réessayer.","Unable to load domain(s). Please try again.":"Impossible de charger le(s) domaine(s). Veuillez réessayer.","Unable to load domain. Please try again.":"Impossible de charger le domaine. Veuillez réessayer.","Unable to load full pass data. Please try again.":"Impossible de charger les données complètes de la passe. Veuillez réessayer.","Unable to load log(s). Please try again.":"Impossible de charger le(s) journal(s). Veuillez réessayer.","Unable to load log. Please try again.":"Impossible de charger le journal. Veuillez réessayer.","Unable to load mail summary. Please try again.":"Impossible de charger le résumé du courrier. Veuillez réessayer.","Unable to load organization domain statuses. Please try again.":"Impossible de charger les statuts des domaines d'organisation. Veuillez réessayer.","Unable to load organization(s). Please try again.":"Impossible de charger l'organisation (s). Veuillez réessayer.","Unable to load owner information. Please try again.":"Impossible de charger les informations sur le propriétaire. Veuillez réessayer.","Unable to load summary. Please try again.":"Impossible de charger le résumé. Veuillez réessayer.","Unable to load tags(s). Please try again.":"Impossible de charger les balises. Veuillez réessayer.","Unable to load user(s). Please try again.":"Impossible de charger le(s) utilisateur(s). Veuillez réessayer.","Unable to load verified domain(s). Please try again.":"Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.","Unable to load verified organization(s). Please try again.":"Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.","Unable to load web connections summary. Please try again.":"Impossible de charger le résumé des connexions web. Veuillez réessayer.","Unable to load web scan(s). Please try again.":"Impossible de charger le(s) scan(s) web. Veuillez réessayer.","Unable to load web summary. Please try again.":"Impossible de charger le résumé web. Veuillez réessayer.","Unable to query affiliation(s). Please try again.":"Impossible de demander l'affiliation (s). Veuillez réessayer.","Unable to query domain(s). Please try again.":"Impossible d'interroger le(s) domaine(s). Veuillez réessayer.","Unable to query log(s). Please try again.":"Impossible d'interroger le(s) journal(s). Veuillez réessayer.","Unable to query user(s). Please try again.":"Impossible d'interroger le(s) utilisateur(s). Veuillez réessayer.","Unable to refresh tokens, please sign in.":"Impossible de rafraîchir les jetons, veuillez vous connecter.","Unable to remove a user that already does not belong to this organization.":"Impossible de supprimer un utilisateur qui n'appartient déjà plus à cette organisation.","Unable to remove domain from unknown organization.":"Impossible de supprimer le domaine d'une organisation inconnue.","Unable to remove domain. Domain is not part of organization.":"Impossible de supprimer le domaine. Le domaine ne fait pas partie de l'organisation.","Unable to remove domain. Please try again.":"Impossible de supprimer le domaine. Veuillez réessayer.","Unable to remove domains from unknown organization.":"Impossible de supprimer les domaines d'une organisation inconnue.","Unable to remove organization. Please try again.":"Impossible de supprimer l'organisation. Veuillez réessayer.","Unable to remove phone number. Please try again.":"Impossible de supprimer le numéro de téléphone. Veuillez réessayer.","Unable to remove unknown domain.":"Impossible de supprimer un domaine inconnu.","Unable to remove unknown organization.":"Impossible de supprimer une organisation inconnue.","Unable to remove unknown user from organization.":"Impossible de supprimer un utilisateur inconnu de l'organisation.","Unable to remove user from organization.":"Impossible de supprimer un utilisateur de l'organisation.","Unable to remove user from this organization. Please try again.":"Impossible de supprimer l'utilisateur de cette organisation. Veuillez réessayer.","Unable to remove user from unknown organization.":"Impossible de supprimer un utilisateur d'une organisation inconnue.","Unable to request a one time scan on a domain that already has a pending scan.":"Impossible de demander une analyse unique sur un domaine qui a déjà une analyse en cours.","Unable to request a one time scan on an unknown domain.":"Impossible de demander un scan unique sur un domaine inconnu.","Unable to request a one time scan. Please try again.":"Impossible de demander une analyse unique. Veuillez réessayer.","Unable to request a subdomain discovery scan on an invalid domain.":"Impossible de demander un scan de découverte de sous-domaine sur un domaine invalide.","Unable to request a subdomain discovery scan on an unknown domain.":"Impossible de demander une analyse de découverte de sous-domaine sur un domaine inconnu.","Unable to request invite to organization with which you are already affiliated.":"Impossible de demander une invitation à une organisation à laquelle vous êtes déjà affilié.","Unable to request invite to organization with which you have already requested to join.":"Impossible de demander une invitation à une organisation à laquelle vous avez déjà demandé à adhérer.","Unable to request invite to unknown organization.":"Impossible de demander une invitation à une organisation inconnue.","Unable to request invite. Please try again.":"Impossible de demander une invitation. Veuillez réessayer.","Unable to reset password. Please request a new email.":"Impossible de réinitialiser le mot de passe. Veuillez demander un nouvel e-mail.","Unable to reset password. Please try again.":"Impossible de réinitialiser le mot de passe. Veuillez réessayer.","Unable to retrieve DMARC report information for: {domain}":["Impossible de récupérer les informations du rapport DMARC pour : ",["domain"]],"Unable to select DMARC report(s) for this period and year.":"Impossible de sélectionner le(s) rapport(s) DMARC pour cette période et cette année.","Unable to send authentication email. Please try again.":"Impossible d'envoyer l'email d'authentification. Veuillez réessayer.","Unable to send authentication text message. Please try again.":"Impossible d'envoyer un message texte d'authentification. Veuillez réessayer.","Unable to send org invite email. Please try again.":"Impossible d'envoyer l'e-mail d'invitation à l'org. Veuillez réessayer.","Unable to send org invite request email. Please try again.":"Impossible d'envoyer l'email de demande d'invitation à l'org. Veuillez réessayer.","Unable to send password reset email. Please try again.":"Impossible d'envoyer l'email de réinitialisation du mot de passe. Veuillez réessayer.","Unable to send two factor authentication message. Please try again.":"Impossible d'envoyer le message d'authentification à deux facteurs. Veuillez réessayer.","Unable to send verification email. Please try again.":"Impossible d'envoyer l'email de vérification. Veuillez réessayer.","Unable to set phone number, please try again.":"Impossible de définir le numéro de téléphone, veuillez réessayer.","Unable to sign in, please try again.":"Impossible de se connecter, veuillez réessayer.","Unable to sign up, please contact org admin for a new invite.":"Impossible de s'inscrire, veuillez contacter l'administrateur de l'organisation pour obtenir une nouvelle invitation.","Unable to sign up. Please try again.":"Impossible de s'inscrire. Veuillez réessayer.","Unable to transfer organization ownership. Please try again.":"Impossible de transférer la propriété de l'organisation. Veuillez réessayer.","Unable to transfer ownership of a verified organization.":"Impossible de transférer la propriété d'une organisation vérifiée.","Unable to transfer ownership of an org to an undefined user.":"Impossible de transférer la propriété d'un org à un utilisateur non défini.","Unable to transfer ownership of undefined organization.":"Impossible de transférer la propriété d'une organisation non définie.","Unable to transfer ownership to a user outside the org. Please invite the user and try again.":"Impossible de transférer la propriété à un utilisateur extérieur à l'org. Veuillez inviter l'utilisateur et réessayer.","Unable to two factor authenticate. Please try again.":"Impossible de s'authentifier par deux facteurs. Veuillez réessayer.","Unable to unfavourite domain, domain is not favourited.":"Impossible de désactiver le domaine, le domaine n'est pas favorisé.","Unable to unfavourite domain. Please try again.":"Impossible de défavoriser le domaine. Veuillez réessayer.","Unable to unfavourite unknown domain.":"Impossible de défavoriser un domaine inconnu.","Unable to update domain edge. Please try again.":"Impossible de mettre à jour le bord du domaine. Veuillez réessayer.","Unable to update domain in an unknown org.":"Impossible de mettre à jour le domaine dans un org inconnu.","Unable to update domain that does not belong to the given organization.":"Impossible de mettre à jour un domaine qui n'appartient pas à l'organisation donnée.","Unable to update domain. Please try again.":"Impossible de mettre à jour le domaine. Veuillez réessayer.","Unable to update organization. Please try again.":"Impossible de mettre à jour l'organisation. Veuillez réessayer.","Unable to update password, current password does not match. Please try again.":"Impossible de mettre à jour le mot de passe, le mot de passe actuel ne correspond pas. Veuillez réessayer.","Unable to update password, new passwords do not match. Please try again.":"Impossible de mettre à jour le mot de passe, les nouveaux mots de passe ne correspondent pas. Veuillez réessayer.","Unable to update password, passwords do not match requirements. Please try again.":"Impossible de mettre à jour le mot de passe, les mots de passe ne correspondent pas aux exigences. Veuillez réessayer.","Unable to update password. Please try again.":"Impossible de mettre à jour le mot de passe. Veuillez réessayer.","Unable to update profile. Please try again.":"Impossible de mettre à jour le profil. Veuillez réessayer.","Unable to update role: organization unknown.":"Impossible de mettre à jour le rôle : organisation inconnue.","Unable to update role: user does not belong to organization.":"Impossible de mettre à jour le rôle : l'utilisateur n'appartient pas à l'organisation.","Unable to update role: user unknown.":"Impossible de mettre à jour le rôle : utilisateur inconnu.","Unable to update unknown domain.":"Impossible de mettre à jour un domaine inconnu.","Unable to update unknown organization.":"Impossible de mettre à jour une organisation inconnue.","Unable to update user's role. Please try again.":"Impossible de mettre à jour le rôle de l'utilisateur. Veuillez réessayer.","Unable to update your own role.":"Impossible de mettre à jour votre propre rôle.","Unable to verify account. Please request a new email.":"Impossible de vérifier le compte. Veuillez demander un nouvel e-mail.","Unable to verify account. Please try again.":"Impossible de vérifier le compte. Veuillez réessayer.","Unable to verify if user is a super admin, please try again.":"Impossible de vérifier si l'utilisateur est un super administrateur, veuillez réessayer.","Unable to verify if user is an admin, please try again.":"Impossible de vérifier si l'utilisateur est un administrateur, veuillez réessayer.","Unable to verify organization. Please try again.":"Impossible de vérifier l'organisation. Veuillez réessayer.","Unable to verify unknown organization.":"Impossible de vérifier une organisation inconnue.","User could not be queried.":"L'utilisateur n'a pas pu être interrogé.","User role was updated successfully.":"Le rôle de l'utilisateur a été mis à jour avec succès.","Username not available, please try another.":"Le nom d'utilisateur n'est pas disponible, veuillez en essayer un autre.","Verification error. Please activate multi-factor authentication to access content.":"Erreur de vérification. Veuillez activer l'authentification multifactorielle pour accéder au contenu.","Verification error. Please verify your account via email to access content.":"Erreur de vérification. Veuillez vérifier votre compte par e-mail pour accéder au contenu.","You must provide a `first` or `last` value to properly paginate the `Affiliation` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Affiliation`.","You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DKIMResults`.","You must provide a `first` or `last` value to properly paginate the `DKIM` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DKIM`.","You must provide a `first` or `last` value to properly paginate the `DMARC` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DMARC`.","You must provide a `first` or `last` value to properly paginate the `DkimFailureTable` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DkimFailureTable`.","You must provide a `first` or `last` value to properly paginate the `DmarcFailureTable` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DmarcFailureTable`.","You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DmarcSummaries`.","You must provide a `first` or `last` value to properly paginate the `Domain` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Domain`.","You must provide a `first` or `last` value to properly paginate the `FullPassTable` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `FullPassTable`.","You must provide a `first` or `last` value to properly paginate the `GuidanceTag` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `GuidanceTag`.","You must provide a `first` or `last` value to properly paginate the `HTTPS` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `HTTPS`.","You must provide a `first` or `last` value to properly paginate the `Log` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Log`.","You must provide a `first` or `last` value to properly paginate the `Organization` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Organization`.","You must provide a `first` or `last` value to properly paginate the `SPF` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SPF`.","You must provide a `first` or `last` value to properly paginate the `SSL` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SSL`.","You must provide a `first` or `last` value to properly paginate the `SpfFailureTable` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SpfFailureTable`.","You must provide a `first` or `last` value to properly paginate the `User` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `User`.","You must provide a `first` or `last` value to properly paginate the `VerifiedDomain` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `VerifiedDomain`.","You must provide a `first` or `last` value to properly paginate the `VerifiedOrganization` connection.":"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `VerifiedOrganization`.","You must provide a `limit` value in the range of 1-100 to properly paginate the `DNS` connection.":"Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `DNS`.","You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection.":"Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `MXRecord`.","You must provide a `limit` value in the range of 1-100 to properly paginate the `web` connection.":"Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `web`.","You must provide a `limit` value to properly paginate the `DNS` connection.":"Vous devez fournir une valeur `limit` pour paginer correctement la connexion `DNS`.","You must provide a `limit` value to properly paginate the `MXRecord` connection.":"Vous devez fournir une valeur `limit` pour paginer correctement la connexion `MXRecord`.","You must provide a `limit` value to properly paginate the `web` connection.":"Vous devez fournir une valeur `limit` pour paginer correctement la connexion `web`.","You must provide a `period` value to access the `DmarcSummaries` connection.":"Vous devez fournir une valeur `period` pour accéder à la connexion `DmarcSummaries`.","You must provide a `year` value to access the `DmarcSummaries` connection.":"Vous devez fournir une valeur `year` pour accéder à la connexion `DmarcSummaries`.","You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `DNS` connection.":"Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `DNS`.","You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection.":"Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `MXRecord`.","You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `web` connection.":"Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `web`.","`{argSet}` must be of type `number` not `{typeSet}`.":["`",["argSet"],"` doit être de type `number` et non `",["typeSet"],"`."],"`{argSet}` on the `Affiliation` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `Affiliation` ne peut être inférieur à zéro."],"`{argSet}` on the `DKIMResults` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `DKIMResults` ne peut être inférieur à zéro."],"`{argSet}` on the `DKIM` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `DKIM` ne peut être inférieur à zéro."],"`{argSet}` on the `DMARC` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `DMARC` ne peut être inférieur à zéro."],"`{argSet}` on the `DkimFailureTable` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro."],"`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro."],"`{argSet}` on the `DmarcSummaries` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro."],"`{argSet}` on the `Domain` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `Domain` ne peut être inférieur à zéro."],"`{argSet}` on the `FullPassTable` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `FullPassTable` ne peut être inférieur à zéro."],"`{argSet}` on the `GuidanceTag` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `GuidanceTag` ne peut être inférieure à zéro."],"`{argSet}` on the `HTTPS` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `HTTPS` ne peut être inférieur à zéro."],"`{argSet}` on the `Log` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `Log` ne peut être inférieur à zéro."],"`{argSet}` on the `Organization` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `Organization` ne peut être inférieure à zéro."],"`{argSet}` on the `SPF` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `SPF` ne peut être inférieure à zéro."],"`{argSet}` on the `SSL` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `SSL` ne peut être inférieur à zéro."],"`{argSet}` on the `SpfFailureTable` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro."],"`{argSet}` on the `User` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `User` ne peut être inférieure à zéro."],"`{argSet}` on the `VerifiedDomain` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro."],"`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero.":["`",["argSet"],"` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro."]}}; \ No newline at end of file +/*eslint-disable*/module.exports={messages:JSON.parse("{\"Eoi1qW\":[\"`\",[\"argSet\"],\"` doit être de type `number` et non `\",[\"typeSet\"],\"`.\"],\"uPlrHl\":[\"`\",[\"argSet\"],\"` sur la connexion `Affiliation` ne peut être inférieur à zéro.\"],\"EtaVdR\":[\"`\",[\"argSet\"],\"` sur la connexion `DKIM` ne peut être inférieur à zéro.\"],\"2npn9d\":[\"`\",[\"argSet\"],\"` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro.\"],\"mpaL2O\":[\"`\",[\"argSet\"],\"` sur la connexion `DKIMResults` ne peut être inférieur à zéro.\"],\"2vz4+a\":[\"`\",[\"argSet\"],\"` sur la connexion `DMARC` ne peut être inférieur à zéro.\"],\"ujwWhu\":[\"`\",[\"argSet\"],\"` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro.\"],\"HbNnNq\":[\"`\",[\"argSet\"],\"` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro.\"],\"eSKAjw\":[\"`\",[\"argSet\"],\"` sur la connexion `Domain` ne peut être inférieur à zéro.\"],\"R2ityD\":[\"`\",[\"argSet\"],\"` sur la connexion `FullPassTable` ne peut être inférieur à zéro.\"],\"dqZo9M\":[\"`\",[\"argSet\"],\"` sur la connexion `GuidanceTag` ne peut être inférieure à zéro.\"],\"YXZolK\":[\"`\",[\"argSet\"],\"` sur la connexion `HTTPS` ne peut être inférieur à zéro.\"],\"UACahy\":[\"`\",[\"argSet\"],\"` sur la connexion `Log` ne peut être inférieur à zéro.\"],\"nkUPC9\":[\"`\",[\"argSet\"],\"` sur la connexion `Organization` ne peut être inférieure à zéro.\"],\"Jkk2ml\":[\"`\",[\"argSet\"],\"` sur la connexion `SPF` ne peut être inférieure à zéro.\"],\"Rfl7ba\":[\"`\",[\"argSet\"],\"` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro.\"],\"w6BMWR\":[\"`\",[\"argSet\"],\"` sur la connexion `SSL` ne peut être inférieur à zéro.\"],\"kk1tk/\":[\"`\",[\"argSet\"],\"` sur la connexion `User` ne peut être inférieure à zéro.\"],\"6ZERsG\":[\"`\",[\"argSet\"],\"` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.\"],\"plfOI4\":[\"`\",[\"argSet\"],\"` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.\"],\"w3MeQ2\":\"Erreur d'authentification. Veuillez vous connecter.\",\"9QA26B\":\"Il n'est pas possible de demander des résultats supplémentaires sans autorisation.\",\"OEzMB0\":\"Impossible d'interroger les affiliations sur l'organisation sans l'autorisation de l'administrateur ou plus.\",\"2K0+0j\":\"Impossible d'interroger les journaux d'audit sur l'organisation sans l'autorisation d'administrateur ou plus.\",\"Z2VjXv\":\"Impossible d'interroger les résultats de l'analyse DNS sans autorisation.\",\"SsrKFI\":\"Impossible d'interroger les sélecteurs de domaine sans autorisation.\",\"/fzcPY\":\"Impossible d'interroger les résultats de l'analyse web sans autorisation.\",\"uWrncA\":\"CVE est déjà ignoré pour ce domaine.\",\"messFf\":\"Le CVE n'est pas ignoré dans ce domaine.\",\"K9BF0u\":\"Courriel déjà utilisé.\",\"kr40ez\":\"Erreur lors de la demande d'analyse. Veuillez réessayer.\",\"xty1cI\":\"Si un compte avec ce nom d'utilisateur est trouvé, un lien de réinitialisation du mot de passe se trouvera dans votre boîte de réception.\",\"n3yyu8\":\"Si un compte avec ce nom d'utilisateur est trouvé, un lien de vérification par e-mail sera trouvé dans votre boîte de réception.\",\"YPojZ6\":\"Code TFA incorrect. Veuillez vous reconnecter.\",\"q/Mmq8\":\"La valeur du jeton est incorrecte. Veuillez demander un nouvel e-mail.\",\"MXwSlX\":\"Le nom d'utilisateur ou le mot de passe est incorrect. Veuillez réessayer.\",\"1YEhLV\":\"Jeton invalide, veuillez vous connecter.\",\"ydzoW2\":\"Message rejeté avec succès\",\"6Ylhna\":\"Les nouveaux mots de passe ne correspondent pas.\",\"BwHoFc\":\"Aucune organisation avec le slug fourni n'a pu être trouvée.\",\"MnkZ16\":\"Aucun domaine vérifié avec le domaine fourni n'a pu être trouvé.\",\"L9hADR\":\"Aucune organisation vérifiée avec le slug fourni n'a pu être trouvée.\",\"uYSCJj\":\"L'organisation a déjà été vérifiée.\",\"p20l3d\":\"Le nom de l'organisation est déjà utilisé, veuillez en choisir un autre et réessayer.\",\"SVGjT0\":\"Le nom de l'organisation est déjà utilisé. Veuillez réessayer avec un nom différent.\",\"dndFWA\":\"Erreur de vérification de la propriété. Impossible de demander des informations sur le domaine.\",\"Drhmz3\":\"Passer à la fois `first` et `last` pour paginer la connexion `Affiliation` n'est pas supporté.\",\"85YUUC\":\"Passer à la fois `first` et `last` pour paginer la connexion `DKIMResults` n'est pas supporté.\",\"ELjtwQ\":\"Passer à la fois `first` et `last` pour paginer la connexion `DkimFailureTable` n'est pas supporté.\",\"fh3YJO\":\"Passer à la fois `first` et `last` pour paginer la connexion `DKIMResults` n'est pas supporté.\",\"x4tq+e\":\"Passer à la fois `first` et `last` pour paginer la connexion `DMARC` n'est pas supporté.\",\"594X4Y\":\"Passer à la fois `first` et `last` pour paginer la connexion `DmarcFailureTable` n'est pas supporté.\",\"X0oiNk\":\"Passer à la fois `first` et `last` pour paginer la connexion `DmarcSummaries` n'est pas supporté.\",\"k0nHGG\":\"Passer à la fois `first` et `last` pour paginer la connexion `Domain` n'est pas supporté.\",\"qvXcAG\":\"Passer à la fois `first` et `last` pour paginer la connexion `FullPassTable` n'est pas supporté.\",\"iC7wND\":\"Passer à la fois `first` et `last` pour paginer la connexion `GuidanceTag` n'est pas supporté.\",\"oiwNsK\":\"Passer à la fois `first` et `last` pour paginer la connexion `HTTPS` n'est pas supporté.\",\"UQKJcw\":\"Passer à la fois `first` et `last` pour paginer la connexion `Log` n'est pas supporté.\",\"9QRMXj\":\"Passer à la fois `first` et `last` pour paginer la connexion `Organization` n'est pas supporté.\",\"y0439x\":\"Passer à la fois `first` et `last` pour paginer la connexion `SPF` n'est pas supporté.\",\"kp3U0m\":\"Passer à la fois `first` et `last` pour paginer la connexion `SpfFailureTable` n'est pas supporté.\",\"o4LzIu\":\"Passer à la fois `first` et `last` pour paginer la connexion `SSL` n'est pas supporté.\",\"eMCdJf\":\"Passer à la fois `first` et `last` pour paginer la connexion `User` n'est pas supporté.\",\"9lvjn+\":\"Passer à la fois `first` et `last` pour paginer la connexion `VerifiedDomain` n'est pas supporté.\",\"diyob+\":\"Passer à la fois `first` et `last` pour paginer la connexion `VerifiedOrganization` n'est pas supporté.\",\"t0Yhq7\":\"Le mot de passe ne répond pas aux exigences.\",\"JweT52\":\"Le mot de passe a été réinitialisé avec succès.\",\"U7wcn8\":\"Le mot de passe a été mis à jour avec succès.\",\"fDGOiR\":\"Les mots de passe ne correspondent pas.\",\"VJ90eS\":\"Erreur de vérification des permissions. Impossible de demander des informations sur le domaine.\",\"smjfcW\":\"Permission refusée : Impossible de récupérer l'organisation spécifiée.\",\"6b0Uzo\":\"Permission refusée : L'authentification multifactorielle est requise pour les comptes admin.\",\"d7fLWy\":\"Permission refusée : Veuillez contacter le propriétaire de l'org pour transférer la propriété.\",\"xBt+I5\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur l'archivage des domaines.\",\"qqp3YV\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide afin de supprimer le domaine.\",\"9ty0BN\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des domaines.\",\"XLwFgy\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide afin de supprimer l'organisation.\",\"AJ7T8X\":\"Autorisation refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.\",\"9MP0sV\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.\",\"9/MaeX\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la mise à jour des rôles des utilisateurs.\",\"gJAEM/\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant les invitations d'utilisateurs.\",\"b+aSK2\":\"Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur les changements de rôle des utilisateurs.\",\"MkIId8\":\"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la création du domaine.\",\"zCsJJ+\":\"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la création de domaines.\",\"zXXF5h\":\"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide pour récupérer ce domaine.\",\"wI7ogy\":\"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur l'analyse de ce domaine.\",\"MU2+6C\":\"Autorisation refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine.\",\"WO5Xev\":\"Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'organisation de l'archivage.\",\"uf+M40\":\"Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine.\",\"Sf3vFY\":\"Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à supprimer l'organisation.\",\"kJ+fSd\":\"Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'analyse de ce domaine.\",\"c11gmL\":\"Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à vérifier cette organisation.\",\"Kwq7PZ\":\"Erreur de permission, pas d'administrateur pour cet utilisateur.\",\"ht7RB9\":\"Erreur de permission: Impossible de fermer le compte d'un autre utilisateur.\",\"S3055l\":\"Erreur de permissions. Vous n'avez pas les autorisations suffisantes pour accéder à ces données.\",\"qj6rps\":\"Le numéro de téléphone a été supprimé avec succès.\",\"uEAFen\":\"Le numéro de téléphone a été configuré avec succès, vous recevrez bientôt un message de vérification.\",\"LbgZEx\":\"Veuillez fournir un commentaire lorsque vous ajoutez un domaine externe.\",\"yu5LUi\":\"Le profil a été mis à jour avec succès.\",\"22PG3h\":[\"La demande de \",[\"amount\"],\" enregistrements sur la connexion `DKIM` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"lEZ2x7\":[\"La demande de \",[\"amount\"],\" enregistrements sur la connexion `DKIMResults` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"3kc6YF\":[\"La demande de \",[\"amount\"],\" enregistrements sur la connexion `DMARC` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"wxwMqQ\":[\"La demande de \",[\"amount\"],\" enregistrements sur la connexion `HTTPS` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"qAmoqx\":[\"La demande de \",[\"amount\"],\" enregistrements sur la connexion `SPF` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"JD82Qj\":[\"La demande de \",[\"amount\"],\" enregistrements sur la connexion `SSL` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"ocE6aN\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `Affiliation` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"VF35kt\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `DkimFailureTable` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"fPydkO\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `DkimFailureTable` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"zOGaYb\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `DmarcSummaries` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"oSdPzK\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `Domain` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"sfbRxl\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `FullPassTable` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"9ECkIl\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `GuidanceTag` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"u4JPdU\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `Log` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"UuKFug\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `Organization` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"ytIe4o\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `SpfFailureTable` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"5vNdM3\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `User` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"SfCdEm\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `VerifiedDomain` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"4Vvv6j\":[\"La demande d'enregistrements `\",[\"amount\"],\"` sur la connexion `VerifiedOrganization` dépasse la limite `\",[\"argSet\"],\"` de 100 enregistrements.\"],\"FPrtxO\":[\"Ajouté avec succès le(s) domaine(s) \",[\"domainCount\"],\" à \",[\"0\"],\".\"],\"aNKZ2s\":[\"Ajouté avec succès les domaines \",[\"domainCount\"],\" à \",[\"0\"],\".\"],\"6kD2ow\":[\"Organisation archivée avec succès : \",[\"0\"],\".\"],\"du9Xou\":\"Le compte a été fermé avec succès.\",\"/XMSy3\":\"Un seul balayage a été effectué avec succès.\",\"EknPJ9\":\"L'analyse de découverte du sous-domaine a été effectuée avec succès.\",\"WnhHoM\":\"Réussir à envoyer un email au compte vérifié, et définir la méthode d'envoi de la TFA sur email.\",\"ZekZu1\":\"Envoi d'un courriel à un compte vérifié.\",\"v/sjus\":\"L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé.\",\"NOS/E2\":[\"L'organisation a été quittée avec succès: \",[\"0\"]],\"Mb7h0p\":[\"Supprimé avec succès le(s) domaine(s) \",[\"domainCount\"],\" de \",[\"0\"],\".\"],\"whT9qL\":[\"Suppression réussie des domaines \",[\"domainCount\"],\" de \",[\"0\"],\".\"],\"KJoIYL\":[\"A réussi à supprimer le domaine : \",[\"0\"],\" de \",[\"1\"],\".\"],\"B3bhOU\":[\"A réussi à supprimer le domaine : \",[\"0\"],\" des favoris.\"],\"6UbMHy\":[\"A réussi à supprimer l'organisation : \",[\"0\"],\".\"],\"m3IqsS\":\"L'utilisateur a été retiré de l'organisation avec succès.\",\"whspKB\":\"La demande d'invitation à l'organisation a été effectuée avec succès et un courriel de notification a été envoyé.\",\"hsTfau\":\"Envoi réussi de l'invitation au service, et de l'email de l'organisation.\",\"92cunG\":\"J'ai réussi à me déconnecter.\",\"UhOnUX\":[\"A réussi à transférer la propriété de org: \",[\"0\"],\" à l'utilisateur: \",[\"1\"]],\"QLcXYT\":\"Envoi réussi de l'invitation au service, et de l'email de l'organisation.\",\"0UHOyT\":\"Le numéro de téléphone a été vérifié avec succès, et la méthode d'envoi de la TFA a été réglée sur le texte.\",\"G+Imw/\":\"La valeur du jeton est incorrecte, veuillez vous connecter à nouveau.\",\"bTMB63\":\"Trop de tentatives de connexion ont échoué, veuillez réinitialiser votre mot de passe et réessayer.\",\"KPk8On\":\"Confirmation de l'achèvement du voyage\",\"ghwok7\":\"Le code à deux facteurs est incorrect. Veuillez réessayer.\",\"to2nZU\":\"La longueur du code à deux facteurs est incorrecte. Veuillez réessayer.\",\"4G8XnK\":\"Impossible de quitter l'organisation. Veuillez réessayer.\",\"VtK4v3\":\"Impossible d'ajouter des domaines dans une organisation inconnue.\",\"5c9XSu\":\"Impossible d'archiver l'organisation. Veuillez réessayer.\",\"d0IZcx\":\"Impossible d'archiver une organisation inconnue.\",\"iGBFKx\":\"Impossible de s'authentifier. Veuillez réessayer.\",\"hGYodY\":\"Impossible de vérifier l'autorisation. Veuillez réessayer.\",\"JBqBmK\":\"Impossible de fermer le compte d'un utilisateur non défini.\",\"ASvI35\":\"Impossible de fermer le compte. Veuillez réessayer.\",\"lYCFPq\":\"Impossible de confirmer l'achèvement de la visite. Veuillez réessayer.\",\"JH9/Jq\":\"Impossible de créer un domaine dans une organisation inconnue.\",\"TUFqYc\":\"Impossible de créer le domaine, l'organisation l'a déjà réclamé.\",\"PnTl0X\":\"Impossible de créer un domaine. Veuillez réessayer.\",\"VkUlo7\":\"Impossible de créer des domaines. Veuillez réessayer.\",\"Bk0F/3\":\"Impossible de créer une organisation. Veuillez réessayer.\",\"ggS9AU\":\"Impossible de découvrir les domaines d'une organisation inconnue.\",\"HLFEUv\":\"Impossible de rejeter le message. Veuillez réessayer.\",\"U8WJkK\":\"Impossible d'envoyer un scan unique. Veuillez réessayer.\",\"sX31Qs\":\"Impossible d'exporter l'organisation. Veuillez réessayer.\",\"QDd6b7\":\"Impossible de favoriser le domaine, l'utilisateur l'a déjà favorisé.\",\"+4vwFL\":\"Impossible d'accéder au domaine favori. Veuillez réessayer.\",\"WNzPnw\":\"Impossible de favoriser le domaine inconnu.\",\"lPIuFw\":\"Impossible de trouver le(s) tag(s) d'orientation des agrégats. Veuillez réessayer.\",\"q4hBrf\":\"Impossible de trouver le(s) tag(s) d'orientation DKIM. Veuillez réessayer.\",\"BC7dKY\":\"Impossible de trouver le(s) résultat(s) DKIM. Veuillez réessayer.\",\"C5fP4H\":\"Impossible de trouver le(s) scan(s) DKIM. Veuillez réessayer.\",\"GH27F7\":\"Impossible de trouver le(s) tag(s) d'orientation DMARC. Veuillez réessayer.\",\"ngV4gO\":\"Impossible de trouver le(s) scan(s) DMARC. Veuillez réessayer.\",\"GtHb3j\":\"Impossible de trouver les données de synthèse DMARC. Veuillez réessayer.\",\"ULGdf/\":\"Impossible de trouver le(s) scan(s) DNS. Veuillez réessayer.\",\"kzp3/l\":\"Impossible de trouver les étiquettes d'orientation. Veuillez réessayer.\",\"lvuEwe\":\"Impossible de trouver la ou les balises d'orientation HTTPS. Veuillez réessayer.\",\"eEnZq2\":\"Impossible de trouver le(s) scan(s) HTTPS. Veuillez réessayer.\",\"8UI/ez\":\"Impossible de trouver le(s) tag(s) d'orientation SPF. Veuillez réessayer.\",\"EJbuMu\":\"Impossible de trouver le(s) scan(s) SPF. Veuillez réessayer.\",\"jy/9PC\":\"Impossible de trouver le(s) tag(s) d'orientation SSL. Veuillez réessayer.\",\"xsn+ev\":\"Impossible de trouver le(s) scan(s) SSL. Veuillez réessayer.\",\"itNwLs\":\"Impossible de trouver le domaine demandé.\",\"AzNyVV\":\"Impossible de trouver l'affiliation de l'utilisateur (s). Veuillez réessayer.\",\"usyHv4\":\"Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.\",\"J0A2iO\":\"Impossible d'ignorer le CVE. Veuillez réessayer.\",\"QlRbEs\":\"Impossible d'inviter un utilisateur dans une organisation. Veuillez réessayer.\",\"Y3CReu\":\"Impossible d'inviter un utilisateur dans une organisation. L'utilisateur est déjà affilié à l'organisation.\",\"4YaHho\":\"Impossible d'inviter un utilisateur à une organisation inconnue.\",\"6DMjZ8\":\"Impossible d'inviter un utilisateur. Veuillez réessayer.\",\"mFUvni\":\"Impossible de s'inviter à un org.\",\"RuV5QP\":\"Impossible de quitter l'organisation. Veuillez réessayer.\",\"pAAJtC\":\"Impossible de quitter une organisation non définie.\",\"NRzPen\":\"Impossible de charger des résultats supplémentaires. Veuillez réessayer.\",\"Q2fyq2\":\"Impossible de charger les informations d'affiliation. Veuillez réessayer.\",\"dLE6X6\":\"Impossible de charger l'affiliation (s). Veuillez réessayer.\",\"tEuM0Y\":\"Impossible de charger le(s) tag(s) d'orientation des agrégats. Veuillez réessayer.\",\"z6WNrg\":\"Impossible de charger tous les statuts de domaine d'organisation. Veuillez réessayer.\",\"AS+XYm\":\"Impossible de charger les données du résumé du graphique. Veuillez réessayer.\",\"VZSEkv\":\"Impossible de charger les données d'échec DKIM. Veuillez réessayer.\",\"iHCZPz\":\"Impossible de charger le(s) tag(s) d'orientation DKIM. Veuillez réessayer.\",\"VYgxhl\":\"Impossible de charger le(s) résultat(s) DKIM. Veuillez réessayer.\",\"XmHD0n\":\"Impossible de charger le(s) scan(s) DKIM. Veuillez réessayer.\",\"H/foxs\":\"Impossible de charger le résumé DKIM. Veuillez réessayer.\",\"DZfpKQ\":\"Impossible de charger les données d'échec DMARC. Veuillez réessayer.\",\"IWleX/\":\"Impossible de charger le(s) tag(s) d'orientation DMARC. Veuillez réessayer.\",\"9KOsEh\":\"Impossible de charger le résumé DMARC. Veuillez réessayer.\",\"eNPs30\":\"Impossible de charger le(s) scan(s) DMARC. Veuillez réessayer.\",\"wKC+/J\":\"Impossible de charger les données de synthèse DMARC. Veuillez réessayer.\",\"PF8L8T\":\"Impossible de charger le résumé DMARC. Veuillez réessayer.\",\"zv2kI4\":\"Impossible de charger le(s) scan(s) DNS. Veuillez réessayer.\",\"+mdFBt\":\"Impossible de charger le(s) sélecteur(s) de domaine. Veuillez réessayer.\",\"hpeR8m\":\"Impossible de charger le domaine. Veuillez réessayer.\",\"f/tKr7\":\"Impossible de charger le(s) domaine(s). Veuillez réessayer.\",\"aHKeg4\":\"Impossible de charger les données complètes de la passe. Veuillez réessayer.\",\"3fphqj\":\"Impossible de charger le(s) tag(s) d'orientation. Veuillez réessayer.\",\"cCar8N\":\"Impossible de charger la ou les balises d'orientation HTTPS. Veuillez réessayer.\",\"7A73DJ\":\"Impossible de charger le(s) scan(s) HTTPS. Veuillez réessayer.\",\"BMKKFn\":\"Impossible de charger le résumé HTTPS. Veuillez réessayer.\",\"hZEouI\":\"Impossible de charger le journal. Veuillez réessayer.\",\"lSzsy0\":\"Impossible de charger le(s) journal(s). Veuillez réessayer.\",\"wrwKt2\":\"Impossible de charger le résumé du courrier. Veuillez réessayer.\",\"Fpm2AP\":\"Impossible de charger les statuts des domaines d'organisation. Veuillez réessayer.\",\"p6Lm32\":\"Impossible de charger les données de synthèse de l'organisation. Veuillez réessayer.\",\"8KT8ti\":\"Impossible de charger l'organisation (s). Veuillez réessayer.\",\"LvHBFL\":\"Impossible de charger les informations sur le propriétaire. Veuillez réessayer.\",\"sXOtBn\":\"Impossible de charger les données d'échec SPF. Veuillez réessayer.\",\"IQkbNl\":\"Impossible de charger le(s) tag(s) d'orientation SPF. Veuillez réessayer.\",\"FHxRJZ\":\"Impossible de charger le(s) scan(s) SPF. Veuillez réessayer.\",\"ZsscmB\":\"Impossible de charger le résumé SPF. Veuillez réessayer.\",\"HI7CSw\":\"Impossible de charger le(s) tag(s) d'orientation SSL. Veuillez réessayer.\",\"tJYOji\":\"Impossible de charger le(s) scan(s) SSL. Veuillez réessayer.\",\"PpwpCp\":\"Impossible de charger le résumé SSL. Veuillez réessayer.\",\"xzKtay\":\"Impossible de charger le résumé. Veuillez réessayer.\",\"y7ER1b\":\"Impossible de charger les balises. Veuillez réessayer.\",\"PzFOAc\":\"Impossible de charger le(s) utilisateur(s). Veuillez réessayer.\",\"Ph65og\":\"Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.\",\"QdFjoo\":\"Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.\",\"MjZQ9y\":\"Impossible de charger les domaines rua vérifiés. Veuillez réessayer.\",\"WMM6DC\":\"Impossible de charger le résumé des connexions web. Veuillez réessayer.\",\"szMW1T\":\"Impossible de charger le(s) scan(s) web. Veuillez réessayer.\",\"OEKyZJ\":\"Impossible de charger le résumé web. Veuillez réessayer.\",\"j4fHlS\":\"Impossible de demander l'affiliation (s). Veuillez réessayer.\",\"c0mbW+\":\"Impossible d'interroger le(s) domaine(s). Veuillez réessayer.\",\"7W4MNs\":\"Impossible d'interroger le(s) journal(s). Veuillez réessayer.\",\"098Gys\":\"Impossible d'interroger le(s) utilisateur(s). Veuillez réessayer.\",\"KfROBu\":\"Impossible de rafraîchir les jetons, veuillez vous connecter.\",\"Hls3tJ\":\"Impossible de supprimer un utilisateur qui n'appartient déjà plus à cette organisation.\",\"ELijnP\":\"Impossible de supprimer le domaine d'une organisation inconnue.\",\"nuxrPH\":\"Impossible de supprimer le domaine. Le domaine ne fait pas partie de l'organisation.\",\"hHXji3\":\"Impossible de supprimer le domaine. Veuillez réessayer.\",\"iMzlxt\":\"Impossible de supprimer les domaines d'une organisation inconnue.\",\"X34ZOQ\":\"Impossible de supprimer l'organisation. Veuillez réessayer.\",\"Qf8OvV\":\"Impossible de supprimer le numéro de téléphone. Veuillez réessayer.\",\"GXX5w7\":\"Impossible de supprimer un domaine inconnu.\",\"pQ0cLH\":\"Impossible de supprimer une organisation inconnue.\",\"gWw+wU\":\"Impossible de supprimer un utilisateur inconnu de l'organisation.\",\"APaKFI\":\"Impossible de supprimer un utilisateur de l'organisation.\",\"v28SUT\":\"Impossible de supprimer l'utilisateur de cette organisation. Veuillez réessayer.\",\"qjq1fL\":\"Impossible de supprimer un utilisateur d'une organisation inconnue.\",\"Wu3IdK\":\"Impossible de demander une analyse unique sur un domaine qui a déjà une analyse en cours.\",\"s79a/Q\":\"Impossible de demander un scan unique sur un domaine inconnu.\",\"BlpeBr\":\"Impossible de demander une analyse unique. Veuillez réessayer.\",\"YlWZ/S\":\"Impossible de demander un scan de découverte de sous-domaine sur un domaine invalide.\",\"/rRkGm\":\"Impossible de demander une analyse de découverte de sous-domaine sur un domaine inconnu.\",\"qTisBR\":\"Impossible de demander une invitation à une organisation à laquelle vous êtes déjà affilié.\",\"1+X6yw\":\"Impossible de demander une invitation à une organisation à laquelle vous avez déjà demandé à adhérer.\",\"Djnfqd\":\"Impossible de demander une invitation à une organisation inconnue.\",\"LlyhW+\":\"Impossible de demander une invitation. Veuillez réessayer.\",\"NQWBIw\":\"Impossible de réinitialiser le mot de passe. Veuillez demander un nouvel e-mail.\",\"dJ2lzx\":\"Impossible de réinitialiser le mot de passe. Veuillez réessayer.\",\"t2wESI\":[\"Impossible de récupérer les informations du rapport DMARC pour : \",[\"domain\"]],\"OTycVQ\":\"Impossible de sélectionner le(s) rapport(s) DMARC pour cette période et cette année.\",\"qT4MFW\":\"Impossible d'envoyer l'email d'authentification. Veuillez réessayer.\",\"LPodG6\":\"Impossible d'envoyer un message texte d'authentification. Veuillez réessayer.\",\"9TsNGX\":\"Impossible d'envoyer l'e-mail d'invitation à l'org. Veuillez réessayer.\",\"zwqufn\":\"Impossible d'envoyer l'email de demande d'invitation à l'org. Veuillez réessayer.\",\"1rL9I7\":\"Impossible d'envoyer l'email de réinitialisation du mot de passe. Veuillez réessayer.\",\"svsSOK\":\"Impossible d'envoyer le message d'authentification à deux facteurs. Veuillez réessayer.\",\"ujyB0i\":\"Impossible d'envoyer l'email de mise à jour du nom d'utilisateur. Veuillez réessayer.\",\"02Keb+\":\"Impossible d'envoyer l'email de vérification. Veuillez réessayer.\",\"E4JeKk\":\"Impossible de définir le numéro de téléphone, veuillez réessayer.\",\"g3Svfo\":\"Impossible de se connecter, veuillez réessayer.\",\"VqhI6Q\":\"Impossible de s'inscrire, veuillez contacter l'administrateur de l'organisation pour obtenir une nouvelle invitation.\",\"2hEUmM\":\"Impossible de s'inscrire. Veuillez réessayer.\",\"7/HZU+\":\"Impossible d'arrêter d'ignorer le CVE. Veuillez réessayer.\",\"JLEqkA\":\"Impossible de transférer la propriété de l'organisation. Veuillez réessayer.\",\"U1j4ef\":\"Impossible de transférer la propriété d'une organisation vérifiée.\",\"c3WjG6\":\"Impossible de transférer la propriété d'un org à un utilisateur non défini.\",\"TMjnAo\":\"Impossible de transférer la propriété d'une organisation non définie.\",\"rzoBBf\":\"Impossible de transférer la propriété à un utilisateur extérieur à l'org. Veuillez inviter l'utilisateur et réessayer.\",\"8TSaAb\":\"Impossible de s'authentifier par deux facteurs. Veuillez réessayer.\",\"FHUz/I\":\"Impossible de désactiver le domaine, le domaine n'est pas favorisé.\",\"F7RRVM\":\"Impossible de défavoriser le domaine. Veuillez réessayer.\",\"RgT2Fw\":\"Impossible de défavoriser un domaine inconnu.\",\"iM9uiT\":\"Unable to unignore CVE. Please try again.\",\"CmZgVA\":\"Impossible de mettre à jour le bord du domaine. Veuillez réessayer.\",\"gAMNI6\":\"Impossible de mettre à jour le domaine dans un org inconnu.\",\"JbEMFm\":\"Impossible de mettre à jour un domaine qui n'appartient pas à l'organisation donnée.\",\"jHfpyF\":\"Impossible de mettre à jour le domaine. Veuillez réessayer.\",\"0tE9dW\":\"Impossible de mettre à jour l'organisation. Veuillez réessayer.\",\"4Dutt5\":\"Impossible de mettre à jour le mot de passe, le mot de passe actuel ne correspond pas. Veuillez réessayer.\",\"fNY+xk\":\"Impossible de mettre à jour le mot de passe, les nouveaux mots de passe ne correspondent pas. Veuillez réessayer.\",\"TK5yIB\":\"Impossible de mettre à jour le mot de passe, les mots de passe ne correspondent pas aux exigences. Veuillez réessayer.\",\"2OQWjx\":\"Impossible de mettre à jour le mot de passe. Veuillez réessayer.\",\"+q5Rwg\":\"Impossible de mettre à jour le profil. Veuillez réessayer.\",\"Zqql23\":\"Impossible de mettre à jour le rôle : organisation inconnue.\",\"5g5CPf\":\"Impossible de mettre à jour le rôle : l'utilisateur n'appartient pas à l'organisation.\",\"ygexeT\":\"Impossible de mettre à jour le rôle : utilisateur inconnu.\",\"K5a9HQ\":\"Impossible de mettre à jour un domaine inconnu.\",\"4WmaEA\":\"Impossible de mettre à jour une organisation inconnue.\",\"13kdVN\":\"Impossible de mettre à jour le rôle de l'utilisateur. Veuillez réessayer.\",\"xWavp5\":\"Impossible de mettre à jour votre propre rôle.\",\"Vs/Ux1\":\"Impossible de vérifier le compte. Veuillez demander un nouvel e-mail.\",\"Z4+72s\":\"Impossible de vérifier le compte. Veuillez réessayer.\",\"Tqm26J\":\"Impossible de vérifier si l'utilisateur est un super administrateur, veuillez réessayer.\",\"KPWee0\":\"Impossible de vérifier si l'utilisateur est un administrateur, veuillez réessayer.\",\"apJs/U\":\"Impossible de vérifier l'organisation. Veuillez réessayer.\",\"x5sf5T\":\"Impossible de vérifier une organisation inconnue.\",\"sesHCz\":\"L'utilisateur n'a pas pu être interrogé.\",\"czGsbE\":\"L'utilisateur essaie de s'enregistrer dans un environnement de non-production.\",\"Tgpvqi\":\"Le rôle de l'utilisateur a été mis à jour avec succès.\",\"iuCjUb\":\"Le nom d'utilisateur n'est pas disponible, veuillez en essayer un autre.\",\"V/PS9h\":\"Erreur de vérification. Veuillez activer l'authentification multifactorielle pour accéder au contenu.\",\"7zQuGi\":\"Erreur de vérification. Veuillez vérifier votre compte par e-mail pour accéder au contenu.\",\"kMaiy6\":\"Vous devez fournir un `domainId` pour récupérer les résultats supplémentaires d'un domaine.\",\"p+DYLk\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Affiliation`.\",\"c7s2Nw\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DKIM`.\",\"SEEDiW\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DkimFailureTable`.\",\"wlbfWc\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DKIMResults`.\",\"lu3LE3\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DMARC`.\",\"pDllMl\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DmarcFailureTable`.\",\"voJfyc\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DmarcSummaries`.\",\"c8Qbmn\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Domain`.\",\"ZacidG\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `FullPassTable`.\",\"mciz5o\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `GuidanceTag`.\",\"87Zz6g\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `HTTPS`.\",\"co0onA\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Log`.\",\"thP+Yz\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Organization`.\",\"Xfjual\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SPF`.\",\"4s6W/Q\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SpfFailureTable`.\",\"EsdwHf\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SSL`.\",\"N+og2p\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `User`.\",\"EMHIWF\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `VerifiedDomain`.\",\"RMtxoi\":\"Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `VerifiedOrganization`.\",\"3Zdrst\":\"Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `DNS`.\",\"DRToPU\":\"Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `MXRecord`.\",\"NfXI/l\":\"Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `web`.\",\"KAZHf6\":\"Vous devez fournir une valeur `limit` pour paginer correctement la connexion `DNS`.\",\"uTt7kN\":\"Vous devez fournir une valeur `limit` pour paginer correctement la connexion `MXRecord`.\",\"KyJdrc\":\"Vous devez fournir une valeur `limit` pour paginer correctement la connexion `web`.\",\"TAJGTA\":\"Vous devez fournir une valeur `period` pour accéder à la connexion `ChartSummaries`.\",\"DWa4my\":\"Vous devez fournir une valeur `period` pour accéder à la connexion `DmarcSummaries`.\",\"XZZ7Lv\":\"Vous devez fournir une valeur `period` pour accéder à la connexion `OrganizationSummaries`.\",\"oafMGl\":\"Vous devez fournir une valeur `year` pour accéder à la connexion `ChartSummaries`.\",\"jqcUEU\":\"Vous devez fournir une valeur `year` pour accéder à la connexion `DmarcSummaries`.\",\"z4RkNn\":\"Vous devez fournir une valeur `year` pour accéder à la connexion `OrganizationSummaries`.\",\"JOssnw\":\"Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `DNS`.\",\"cfpKpu\":\"Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `MXRecord`.\",\"O++odO\":\"Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `web`.\"}")}; \ No newline at end of file diff --git a/api/src/locale/fr/messages.po b/api/src/locale/fr/messages.po index 2cb099e18..94b17afd7 100644 --- a/api/src/locale/fr/messages.po +++ b/api/src/locale/fr/messages.po @@ -11,75 +11,226 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#: src/auth/check-permission.js:18 -#: src/auth/check-permission.js:57 -#: src/auth/user-required.js:10 -#: src/auth/user-required.js:21 -#: src/auth/user-required.js:28 +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:121 +#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:208 +#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:131 +#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:83 +#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:83 +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:202 +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:83 +#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:83 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:178 +#: src/domain/loaders/load-domain-connections-by-user-id.js:205 +#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:132 +#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:136 +#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:136 +#: src/organization/loaders/load-organization-connections-by-domain-id.js:203 +#: src/organization/loaders/load-organization-connections-by-user-id.js:213 +#: src/user/loaders/load-user-connections-by-user-id.js:145 +#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:164 +#: src/verified-domains/loaders/load-verified-domain-connections.js:164 +#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:214 +#: src/verified-organizations/loaders/load-verified-organizations-connections.js:212 +msgid "`{argSet}` must be of type `number` not `{typeSet}`." +msgstr "`{argSet}` doit être de type `number` et non `{typeSet}`." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:98 +#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:182 +msgid "`{argSet}` on the `Affiliation` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `Affiliation` ne peut être inférieur à zéro." + +#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:135 +#~ msgid "`{argSet}` on the `DKIM` connection cannot be less than zero." +#~ msgstr "`{argSet}` sur la connexion `DKIM` ne peut être inférieur à zéro." + +#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `DkimFailureTable` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro." + +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:110 +#~ msgid "`{argSet}` on the `DKIMResults` connection cannot be less than zero." +#~ msgstr "`{argSet}` sur la connexion `DKIMResults` ne peut être inférieur à zéro." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:160 +#~ msgid "`{argSet}` on the `DMARC` connection cannot be less than zero." +#~ msgstr "`{argSet}` sur la connexion `DMARC` ne peut être inférieur à zéro." + +#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro." + +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:179 +msgid "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro." + +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:157 +#: src/domain/loaders/load-domain-connections-by-user-id.js:182 +msgid "`{argSet}` on the `Domain` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `Domain` ne peut être inférieur à zéro." + +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `FullPassTable` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `FullPassTable` ne peut être inférieur à zéro." + +#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:106 +#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:110 +#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:110 +msgid "`{argSet}` on the `GuidanceTag` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `GuidanceTag` ne peut être inférieure à zéro." + +#: src/web-scan/loaders/load-https-connections-by-domain-id.js:168 +#~ msgid "`{argSet}` on the `HTTPS` connection cannot be less than zero." +#~ msgstr "`{argSet}` sur la connexion `HTTPS` ne peut être inférieur à zéro." + +#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:112 +msgid "`{argSet}` on the `Log` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `Log` ne peut être inférieur à zéro." + +#: src/organization/loaders/load-organization-connections-by-domain-id.js:182 +#: src/organization/loaders/load-organization-connections-by-user-id.js:192 +msgid "`{argSet}` on the `Organization` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `Organization` ne peut être inférieure à zéro." + +#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:154 +#~ msgid "`{argSet}` on the `SPF` connection cannot be less than zero." +#~ msgstr "`{argSet}` sur la connexion `SPF` ne peut être inférieure à zéro." + +#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:57 +msgid "`{argSet}` on the `SpfFailureTable` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro." + +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:192 +#~ msgid "`{argSet}` on the `SSL` connection cannot be less than zero." +#~ msgstr "`{argSet}` sur la connexion `SSL` ne peut être inférieur à zéro." + +#: src/user/loaders/load-user-connections-by-user-id.js:122 +msgid "`{argSet}` on the `User` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `User` ne peut être inférieure à zéro." + +#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:138 +#: src/verified-domains/loaders/load-verified-domain-connections.js:138 +msgid "`{argSet}` on the `VerifiedDomain` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro." + +#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:188 +#: src/verified-organizations/loaders/load-verified-organizations-connections.js:186 +msgid "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." +msgstr "`{argSet}` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro." + +#: src/organization/objects/organization.js:240 +#: src/organization/queries/get-all-organization-domain-statuses.js:69 +msgid "Assess" +msgstr "Évaluez" + +#: src/auth/checks/check-permission.js:18 +#: src/auth/checks/check-permission.js:57 +#: src/auth/guards/user-required.js:10 +#: src/auth/guards/user-required.js:21 +#: src/auth/guards/user-required.js:28 +#: src/auth/loaders/load-permission-by-org-id.js:19 +#: src/auth/loaders/load-permission-by-org-id.js:63 msgid "Authentication error. Please sign in." msgstr "Erreur d'authentification. Veuillez vous connecter." -#: src/organization/objects/organization.js:296 +#: src/domain/objects/domain.js:229 +msgid "Cannot query additional findings without permission." +msgstr "Il n'est pas possible de demander des résultats supplémentaires sans autorisation." + +#: src/organization/objects/organization.js:359 msgid "Cannot query affiliations on organization without admin permission or higher." msgstr "Impossible d'interroger les affiliations sur l'organisation sans l'autorisation de l'administrateur ou plus." #: src/audit-logs/loaders/load-audit-logs-by-org-id.js:224 -#: src/audit-logs/queries/find-audit-logs.js:51 +#: src/audit-logs/queries/find-audit-logs.js:53 msgid "Cannot query audit logs on organization without admin permission or higher." msgstr "Impossible d'interroger les journaux d'audit sur l'organisation sans l'autorisation d'administrateur ou plus." -#: src/domain/objects/domain.js:161 +#: src/domain/objects/domain.js:164 msgid "Cannot query dns scan results without permission." msgstr "Impossible d'interroger les résultats de l'analyse DNS sans autorisation." -#: src/domain/objects/domain.js:63 +#: src/domain/objects/domain.js:65 msgid "Cannot query domain selectors without permission." msgstr "Impossible d'interroger les sélecteurs de domaine sans autorisation." -#: src/domain/objects/domain.js:203 -#: src/domain/objects/domain.js:249 +#: src/domain/objects/domain.js:206 msgid "Cannot query web scan results without permission." msgstr "Impossible d'interroger les résultats de l'analyse web sans autorisation." -#: src/user/mutations/sign-up.js:104 +#: src/domain/mutations/ignore-cve.js:77 +msgid "CVE is already ignored for this domain." +msgstr "CVE est déjà ignoré pour ce domaine." + +#: src/domain/mutations/unignore-cve.js:77 +msgid "CVE is not ignored for this domain." +msgstr "Le CVE n'est pas ignoré dans ce domaine." + +#: src/organization/objects/organization.js:242 +#: src/organization/queries/get-all-organization-domain-statuses.js:71 +msgid "Deploy" +msgstr "msgstr Déployer" + +#: src/user/mutations/sign-up.js:111 msgid "Email already in use." msgstr "Courriel déjà utilisé." -#: src/domain/mutations/request-scan.js:87 -#: src/domain/mutations/request-scan.js:97 +#: src/organization/objects/organization.js:244 +#: src/organization/queries/get-all-organization-domain-statuses.js:73 +msgid "Enforce" +msgstr "Appliquer" + +#: src/domain/mutations/request-scan.js:90 +#: src/domain/mutations/request-scan.js:100 msgid "Error while requesting scan. Please try again." msgstr "Erreur lors de la demande d'analyse. Veuillez réessayer." -#: src/user/mutations/send-password-reset.js:57 +#: src/user/mutations/send-password-reset.js:61 msgid "If an account with this username is found, a password reset link will be found in your inbox." msgstr "Si un compte avec ce nom d'utilisateur est trouvé, un lien de réinitialisation du mot de passe se trouvera dans votre boîte de réception." -#: src/user/mutations/send-email-verification.js:59 -msgid "If an account with this username is found, an email verification link will be found in your inbox." -msgstr "Si un compte avec ce nom d'utilisateur est trouvé, un lien de vérification par e-mail sera trouvé dans votre boîte de réception." +#: src/user/mutations/send-email-verification.js:60 +#~ msgid "If an account with this username is found, an email verification link will be found in your inbox." +#~ msgstr "Si un compte avec ce nom d'utilisateur est trouvé, un lien de vérification par e-mail sera trouvé dans votre boîte de réception." -#: src/user/mutations/authenticate.js:186 +#: src/user/mutations/authenticate.js:221 +#: src/user/mutations/authenticate.js:229 +#: src/user/mutations/authenticate.js:231 msgid "Incorrect TFA code. Please sign in again." msgstr "Code TFA incorrect. Veuillez vous reconnecter." -#: src/user/mutations/reset-password.js:59 +#: src/user/mutations/reset-password.js:61 msgid "Incorrect token value. Please request a new email." msgstr "La valeur du jeton est incorrecte. Veuillez demander un nouvel e-mail." -#: src/user/mutations/sign-in.js:66 -#: src/user/mutations/sign-in.js:260 +#: src/user/mutations/sign-in.js:68 +#: src/user/mutations/sign-in.js:285 msgid "Incorrect username or password. Please try again." msgstr "Le nom d'utilisateur ou le mot de passe est incorrect. Veuillez réessayer." -#: src/auth/verify-jwt.js:15 +#: src/auth/utils/verify-jwt.js:15 msgid "Invalid token, please sign in." msgstr "Jeton invalide, veuillez vous connecter." -#: src/user/mutations/reset-password.js:98 +#: src/organization/objects/organization.js:246 +#: src/organization/queries/get-all-organization-domain-statuses.js:75 +msgid "Maintain" +msgstr "Maintenir" + +#: src/user/mutations/dismiss-message.js:75 +msgid "Message dismissed successfully" +msgstr "Message rejeté avec succès" + +#: src/user/mutations/reset-password.js:88 msgid "New passwords do not match." msgstr "Les nouveaux mots de passe ne correspondent pas." -#: src/organization/queries/find-organization-by-slug.js:41 +#: src/organization/queries/find-organization-by-slug.js:42 #: src/user/queries/find-my-tracker.js:29 msgid "No organization with the provided slug could be found." msgstr "Aucune organisation avec le slug fourni n'a pu être trouvée." @@ -92,47 +243,47 @@ msgstr "Aucun domaine vérifié avec le domaine fourni n'a pu être trouvé." msgid "No verified organization with the provided slug could be found." msgstr "Aucune organisation vérifiée avec le slug fourni n'a pu être trouvée." -#: src/organization/mutations/verify-organization.js:81 +#: src/organization/mutations/verify-organization.js:78 msgid "Organization has already been verified." msgstr "L'organisation a déjà été vérifiée." -#: src/organization/mutations/update-organization.js:174 +#: src/organization/mutations/update-organization.js:167 msgid "Organization name already in use, please choose another and try again." msgstr "Le nom de l'organisation est déjà utilisé, veuillez en choisir un autre et réessayer." -#: src/organization/mutations/create-organization.js:124 +#: src/organization/mutations/create-organization.js:85 msgid "Organization name already in use. Please try again with a different name." msgstr "Le nom de l'organisation est déjà utilisé. Veuillez réessayer avec un nom différent." -#: src/auth/check-domain-ownership.js:29 -#: src/auth/check-domain-ownership.js:39 -#: src/auth/check-domain-ownership.js:65 -#: src/auth/check-domain-ownership.js:74 +#: src/auth/checks/check-domain-ownership.js:29 +#: src/auth/checks/check-domain-ownership.js:39 +#: src/auth/checks/check-domain-ownership.js:65 +#: src/auth/checks/check-domain-ownership.js:74 msgid "Ownership check error. Unable to request domain information." msgstr "Erreur de vérification de la propriété. Impossible de demander des informations sur le domaine." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:77 +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:89 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:170 msgid "Passing both `first` and `last` to paginate the `Affiliation` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `Affiliation` n'est pas supporté." -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:98 -#~ msgid "Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported." -#~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DKIMResults` n'est pas supporté." - #: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:123 #: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:159 #~ msgid "Passing both `first` and `last` to paginate the `DKIM` connection is not supported." #~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DKIMResults` n'est pas supporté." -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:147 -#~ msgid "Passing both `first` and `last` to paginate the `DMARC` connection is not supported." -#~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DMARC` n'est pas supporté." - #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:45 msgid "Passing both `first` and `last` to paginate the `DkimFailureTable` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DkimFailureTable` n'est pas supporté." +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:98 +#~ msgid "Passing both `first` and `last` to paginate the `DKIMResults` connection is not supported." +#~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DKIMResults` n'est pas supporté." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:147 +#~ msgid "Passing both `first` and `last` to paginate the `DMARC` connection is not supported." +#~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DMARC` n'est pas supporté." + #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:45 msgid "Passing both `first` and `last` to paginate the `DmarcFailureTable` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DmarcFailureTable` n'est pas supporté." @@ -142,7 +293,7 @@ msgid "Passing both `first` and `last` to paginate the `DmarcSummaries` connecti msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DmarcSummaries` n'est pas supporté." #: src/domain/loaders/load-domain-connections-by-organizations-id.js:148 -#: src/domain/loaders/load-domain-connections-by-user-id.js:160 +#: src/domain/loaders/load-domain-connections-by-user-id.js:173 msgid "Passing both `first` and `last` to paginate the `Domain` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `Domain` n'est pas supporté." @@ -168,7 +319,7 @@ msgid "Passing both `first` and `last` to paginate the `Log` connection is not s msgstr "Passer à la fois `first` et `last` pour paginer la connexion `Log` n'est pas supporté." #: src/organization/loaders/load-organization-connections-by-domain-id.js:173 -#: src/organization/loaders/load-organization-connections-by-user-id.js:173 +#: src/organization/loaders/load-organization-connections-by-user-id.js:185 msgid "Passing both `first` and `last` to paginate the `Organization` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `Organization` n'est pas supporté." @@ -176,15 +327,15 @@ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `Organizat #~ msgid "Passing both `first` and `last` to paginate the `SPF` connection is not supported." #~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `SPF` n'est pas supporté." -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:180 -#~ msgid "Passing both `first` and `last` to paginate the `SSL` connection is not supported." -#~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `SSL` n'est pas supporté." - #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:45 msgid "Passing both `first` and `last` to paginate the `SpfFailureTable` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `SpfFailureTable` n'est pas supporté." -#: src/user/loaders/load-user-connections-by-user-id.js:116 +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:180 +#~ msgid "Passing both `first` and `last` to paginate the `SSL` connection is not supported." +#~ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `SSL` n'est pas supporté." + +#: src/user/loaders/load-user-connections-by-user-id.js:113 msgid "Passing both `first` and `last` to paginate the `User` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `User` n'est pas supporté." @@ -198,29 +349,38 @@ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `VerifiedD msgid "Passing both `first` and `last` to paginate the `VerifiedOrganization` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `VerifiedOrganization` n'est pas supporté." -#: src/user/mutations/reset-password.js:110 -#: src/user/mutations/sign-up.js:82 +#: src/user/mutations/reset-password.js:100 +#: src/user/mutations/sign-up.js:89 msgid "Password does not meet requirements." msgstr "Le mot de passe ne répond pas aux exigences." -#: src/user/mutations/reset-password.js:148 +#: src/user/mutations/reset-password.js:140 msgid "Password was successfully reset." msgstr "Le mot de passe a été réinitialisé avec succès." -#: src/user/mutations/update-user-password.js:107 +#: src/user/mutations/update-user-password.js:109 msgid "Password was successfully updated." msgstr "Le mot de passe a été mis à jour avec succès." -#: src/user/mutations/sign-up.js:92 +#: src/user/mutations/sign-up.js:99 msgid "Passwords do not match." msgstr "Les mots de passe ne correspondent pas." +#: src/auth/checks/check-domain-permission.js:22 +#: src/auth/checks/check-domain-permission.js:51 +#: src/auth/checks/check-domain-permission.js:61 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:19 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:51 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:61 +msgid "Permission check error. Unable to request domain information." +msgstr "Erreur de vérification des permissions. Impossible de demander des informations sur le domaine." + #: src/organization/queries/find-organization-by-slug.js:50 -#: src/organization/queries/find-organization-by-slug.js:55 +#: src/organization/queries/find-organization-by-slug.js:52 msgid "Permission Denied: Could not retrieve specified organization." msgstr "Permission refusée : Impossible de récupérer l'organisation spécifiée." -#: src/user/mutations/update-user-profile.js:110 +#: src/user/mutations/update-user-profile.js:109 msgid "Permission Denied: Multi-factor authentication is required for admin accounts" msgstr "Permission refusée : L'authentification multifactorielle est requise pour les comptes admin." @@ -228,79 +388,99 @@ msgstr "Permission refusée : L'authentification multifactorielle est requise po msgid "Permission Denied: Please contact org owner to transfer ownership." msgstr "Permission refusée : Veuillez contacter le propriétaire de l'org pour transférer la propriété." -#: src/domain/mutations/remove-organizations-domains.js:127 +#: src/domain/mutations/remove-organizations-domains.js:128 msgid "Permission Denied: Please contact organization admin for help with archiving domains." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur l'archivage des domaines." -#: src/domain/mutations/remove-domain.js:94 +#: src/tags/mutations/create-tag.js:131 +msgid "Permission Denied: Please contact organization admin for help with creating tag." +msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la création d'un tag." + +#: src/domain/mutations/remove-domain.js:96 msgid "Permission Denied: Please contact organization admin for help with removing domain." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide afin de supprimer le domaine." -#: src/domain/mutations/remove-organizations-domains.js:116 +#: src/domain/mutations/remove-organizations-domains.js:117 msgid "Permission Denied: Please contact organization admin for help with removing domains." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des domaines." -#: src/organization/mutations/remove-organization.js:69 +#: src/organization/mutations/remove-organization.js:67 msgid "Permission Denied: Please contact organization admin for help with removing organization." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide afin de supprimer l'organisation." -#: src/affiliation/mutations/remove-user-from-org.js:126 -#: src/affiliation/mutations/remove-user-from-org.js:138 +#: src/affiliation/mutations/remove-user-from-org.js:128 +#: src/affiliation/mutations/remove-user-from-org.js:140 msgid "Permission Denied: Please contact organization admin for help with removing users." msgstr "Autorisation refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs." -#: src/organization/mutations/update-organization.js:145 +#: src/domain/mutations/update-domains-by-domain-ids.js:81 +#: src/domain/mutations/update-domains-by-filters.js:89 +msgid "Permission Denied: Please contact organization admin for help with updating domains." +msgstr "Autorisation refusée : veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant la mise à jour des domaines." + +#: src/organization/mutations/update-organization.js:152 msgid "Permission Denied: Please contact organization admin for help with updating organization." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs." +#: src/tags/mutations/update-tag.js:125 +#: src/tags/mutations/update-tag.js:136 +msgid "Permission Denied: Please contact organization admin for help with updating tag." +msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la mise à jour de la balise." + #: src/affiliation/mutations/update-user-role.js:170 #: src/affiliation/mutations/update-user-role.js:193 #: src/affiliation/mutations/update-user-role.js:210 #~ msgid "Permission Denied: Please contact organization admin for help with updating user roles." #~ msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la mise à jour des rôles des utilisateurs." -#: src/affiliation/mutations/invite-user-to-org.js:101 +#: src/affiliation/mutations/invite-user-to-org.js:99 msgid "Permission Denied: Please contact organization admin for help with user invitations." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant les invitations d'utilisateurs." -#: src/affiliation/mutations/update-user-role.js:109 -#: src/affiliation/mutations/update-user-role.js:160 -#: src/affiliation/mutations/update-user-role.js:172 +#: src/affiliation/mutations/update-user-role.js:112 msgid "Permission Denied: Please contact organization admin for help with user role changes." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur les changements de rôle des utilisateurs." -#: src/domain/mutations/create-domain.js:145 +#: src/domain/mutations/create-domain.js:135 msgid "Permission Denied: Please contact organization user for help with creating domain." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la création du domaine." -#: src/domain/mutations/add-organizations-domains.js:125 +#: src/domain/mutations/add-organizations-domains.js:122 msgid "Permission Denied: Please contact organization user for help with creating domains." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la création de domaines." +#: src/organization/objects/organization.js:111 +#~ msgid "Permission Denied: Please contact organization user for help with retrieving tags." +#~ msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide afin de récupérer les étiquettes." + #: src/domain/queries/find-domain-by-domain.js:51 -#: src/organization/objects/organization.js:144 +#: src/organization/objects/organization.js:195 msgid "Permission Denied: Please contact organization user for help with retrieving this domain." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide pour récupérer ce domaine." -#: src/domain/mutations/request-discovery.js:97 -#: src/domain/mutations/request-scan.js:63 +#: src/domain/mutations/request-discovery.js:98 +#: src/domain/mutations/request-scan.js:66 msgid "Permission Denied: Please contact organization user for help with scanning this domain." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur l'analyse de ce domaine." -#: src/domain/mutations/update-domain.js:163 +#: src/domain/mutations/update-domain.js:162 msgid "Permission Denied: Please contact organization user for help with updating this domain." msgstr "Autorisation refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine." -#: src/organization/mutations/archive-organization.js:68 +#: src/organization/mutations/archive-organization.js:66 msgid "Permission Denied: Please contact super admin for help with archiving organization." msgstr "Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'organisation de l'archivage." -#: src/domain/mutations/remove-domain.js:107 -#: src/domain/mutations/remove-organizations-domains.js:105 +#: src/domain/mutations/create-domain.js:157 +msgid "Permission Denied: Please contact super admin for help with creating domain." +msgstr "Permission refusée : Veuillez contacter le super admin pour obtenir de l'aide pour créer le domaine." + +#: src/domain/mutations/remove-domain.js:109 +#: src/domain/mutations/remove-organizations-domains.js:106 msgid "Permission Denied: Please contact super admin for help with removing domain." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine." -#: src/organization/mutations/remove-organization.js:82 +#: src/organization/mutations/remove-organization.js:80 msgid "Permission Denied: Please contact super admin for help with removing organization." msgstr "Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à supprimer l'organisation." @@ -308,50 +488,83 @@ msgstr "Permission refusée : Veuillez contacter le super administrateur pour qu #~ msgid "Permission Denied: Please contact super admin for help with scanning this domain." #~ msgstr "Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'analyse de ce domaine." -#: src/organization/mutations/verify-organization.js:68 +#: src/domain/mutations/update-domain.js:210 +msgid "Permission Denied: Please contact super admin for help with updating domain." +msgstr "Permission refusée : Veuillez contacter le super admin pour obtenir de l'aide pour mettre à jour le domaine." + +#: src/tags/mutations/update-tag.js:147 +msgid "Permission Denied: Please contact super admin for help with updating tag." +msgstr "Autorisation refusée : veuillez contacter l'administrateur principal pour obtenir de l'aide concernant la mise à jour de la balise." + +#: src/affiliation/mutations/invite-user-to-org.js:112 +msgid "Permission Denied: Please contact super admin for help with user invitations." +msgstr "Accès refusé : veuillez contacter l'administrateur principal pour obtenir de l'aide concernant les invitations d'utilisateurs." + +#: src/affiliation/mutations/update-user-role.js:167 +msgid "Permission Denied: Please contact super admin for help with user role changes." +msgstr "Accès refusé : veuillez contacter l'administrateur principal pour obtenir de l'aide concernant la modification des rôles d'utilisateur." + +#: src/organization/mutations/verify-organization.js:65 msgid "Permission Denied: Please contact super admin for help with verifying this organization." msgstr "Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à vérifier cette organisation." -#: src/auth/check-domain-permission.js:22 -#: src/auth/check-domain-permission.js:51 -#: src/auth/check-domain-permission.js:61 -msgid "Permission check error. Unable to request domain information." -msgstr "Erreur de vérification des permissions. Impossible de demander des informations sur le domaine." - -#: src/auth/check-user-is-admin-for-user.js:20 -#: src/auth/check-user-is-admin-for-user.js:30 -#: src/auth/check-user-is-admin-for-user.js:63 -#: src/auth/check-user-is-admin-for-user.js:73 +#: src/auth/checks/check-user-is-admin-for-user.js:20 +#: src/auth/checks/check-user-is-admin-for-user.js:30 +#: src/auth/checks/check-user-is-admin-for-user.js:63 +#: src/auth/checks/check-user-is-admin-for-user.js:73 msgid "Permission error, not an admin for this user." msgstr "Erreur de permission, pas d'administrateur pour cet utilisateur." -#: src/user/mutations/close-account.js:143 +#: src/user/mutations/close-account.js:148 msgid "Permission error: Unable to close other user's account." msgstr "Erreur de permission: Impossible de fermer le compte d'un autre utilisateur." -#: src/auth/super-admin-required.js:11 -#: src/organization/queries/get-all-organization-domain-statuses.js:33 +#: src/auth/guards/super-admin-required.js:11 msgid "Permissions error. You do not have sufficient permissions to access this data." msgstr "Erreur de permissions. Vous n'avez pas les autorisations suffisantes pour accéder à ces données." -#: src/user/mutations/remove-phone-number.js:75 +#: src/user/mutations/remove-phone-number.js:65 msgid "Phone number has been successfully removed." msgstr "Le numéro de téléphone a été supprimé avec succès." -#: src/user/mutations/set-phone-number.js:116 +#: src/user/mutations/set-phone-number.js:118 msgid "Phone number has been successfully set, you will receive a verification text message shortly." msgstr "Le numéro de téléphone a été configuré avec succès, vous recevrez bientôt un message de vérification." #: src/domain/mutations/create-domain.js:118 -#: src/domain/mutations/update-domain.js:120 -msgid "Please provide a comment when adding an outside domain." -msgstr "Veuillez fournir un commentaire lorsque vous ajoutez un domaine externe." +#: src/domain/mutations/update-domain.js:124 +#~ msgid "Please provide a comment when adding an outside domain." +#~ msgstr "Veuillez fournir un commentaire lorsque vous ajoutez un domaine externe." -#: src/user/mutations/update-user-profile.js:184 +#: src/user/mutations/update-user-profile.js:188 msgid "Profile successfully updated." msgstr "Le profil a été mis à jour avec succès." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:95 +#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:146 +#~ msgid "Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "La demande de {amount} enregistrements sur la connexion `DKIM` dépasse la limite `{argSet}` de 100 enregistrements." + +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:121 +#~ msgid "Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "La demande de {amount} enregistrements sur la connexion `DKIMResults` dépasse la limite `{argSet}` de 100 enregistrements." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:171 +#~ msgid "Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "La demande de {amount} enregistrements sur la connexion `DMARC` dépasse la limite `{argSet}` de 100 enregistrements." + +#: src/web-scan/loaders/load-https-connections-by-domain-id.js:179 +#~ msgid "Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "La demande de {amount} enregistrements sur la connexion `HTTPS` dépasse la limite `{argSet}` de 100 enregistrements." + +#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:165 +#~ msgid "Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "La demande de {amount} enregistrements sur la connexion `SPF` dépasse la limite `{argSet}` de 100 enregistrements." + +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:203 +#~ msgid "Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records." +#~ msgstr "La demande de {amount} enregistrements sur la connexion `SSL` dépasse la limite `{argSet}` de 100 enregistrements." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:107 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:193 msgid "Requesting `{amount}` records on the `Affiliation` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `Affiliation` dépasse la limite `{argSet}` de 100 enregistrements." @@ -369,7 +582,7 @@ msgid "Requesting `{amount}` records on the `DmarcSummaries` connection exceeds msgstr "La demande d'enregistrements `{amount}` sur la connexion `DmarcSummaries` dépasse la limite `{argSet}` de 100 enregistrements." #: src/domain/loaders/load-domain-connections-by-organizations-id.js:164 -#: src/domain/loaders/load-domain-connections-by-user-id.js:178 +#: src/domain/loaders/load-domain-connections-by-user-id.js:191 msgid "Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `Domain` dépasse la limite `{argSet}` de 100 enregistrements." @@ -391,7 +604,7 @@ msgid "Requesting `{amount}` records on the `Log` connection exceeds the `{argSe msgstr "La demande d'enregistrements `{amount}` sur la connexion `Log` dépasse la limite `{argSet}` de 100 enregistrements." #: src/organization/loaders/load-organization-connections-by-domain-id.js:189 -#: src/organization/loaders/load-organization-connections-by-user-id.js:187 +#: src/organization/loaders/load-organization-connections-by-user-id.js:199 msgid "Requesting `{amount}` records on the `Organization` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `Organization` dépasse la limite `{argSet}` de 100 enregistrements." @@ -399,7 +612,7 @@ msgstr "La demande d'enregistrements `{amount}` sur la connexion `Organization` msgid "Requesting `{amount}` records on the `SpfFailureTable` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `SpfFailureTable` dépasse la limite `{argSet}` de 100 enregistrements." -#: src/user/loaders/load-user-connections-by-user-id.js:139 +#: src/user/loaders/load-user-connections-by-user-id.js:131 msgid "Requesting `{amount}` records on the `User` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `User` dépasse la limite `{argSet}` de 100 enregistrements." @@ -413,31 +626,8 @@ msgstr "La demande d'enregistrements `{amount}` sur la connexion `VerifiedDomain msgid "Requesting `{amount}` records on the `VerifiedOrganization` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `VerifiedOrganization` dépasse la limite `{argSet}` de 100 enregistrements." -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:121 -#~ msgid "Requesting {amount} records on the `DKIMResults` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "La demande de {amount} enregistrements sur la connexion `DKIMResults` dépasse la limite `{argSet}` de 100 enregistrements." - -#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:146 -#~ msgid "Requesting {amount} records on the `DKIM` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "La demande de {amount} enregistrements sur la connexion `DKIM` dépasse la limite `{argSet}` de 100 enregistrements." - -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:171 -#~ msgid "Requesting {amount} records on the `DMARC` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "La demande de {amount} enregistrements sur la connexion `DMARC` dépasse la limite `{argSet}` de 100 enregistrements." - -#: src/web-scan/loaders/load-https-connections-by-domain-id.js:179 -#~ msgid "Requesting {amount} records on the `HTTPS` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "La demande de {amount} enregistrements sur la connexion `HTTPS` dépasse la limite `{argSet}` de 100 enregistrements." - -#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:165 -#~ msgid "Requesting {amount} records on the `SPF` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "La demande de {amount} enregistrements sur la connexion `SPF` dépasse la limite `{argSet}` de 100 enregistrements." - -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:203 -#~ msgid "Requesting {amount} records on the `SSL` connection exceeds the `{argSet}` limit of 100 records." -#~ msgstr "La demande de {amount} enregistrements sur la connexion `SSL` dépasse la limite `{argSet}` de 100 enregistrements." - -#: src/domain/mutations/add-organizations-domains.js:338 +#. placeholder {0}: org.slug +#: src/domain/mutations/add-organizations-domains.js:334 msgid "Successfully added {domainCount} domain(s) to {0}." msgstr "Ajouté avec succès le(s) domaine(s) {domainCount} à {0}." @@ -445,64 +635,75 @@ msgstr "Ajouté avec succès le(s) domaine(s) {domainCount} à {0}." #~ msgid "Successfully added {domainCount} domains to {0}." #~ msgstr "Ajouté avec succès les domaines {domainCount} à {0}." -#: src/organization/mutations/archive-organization.js:188 +#. placeholder {0}: organization.slug +#: src/organization/mutations/archive-organization.js:100 msgid "Successfully archived organization: {0}." msgstr "Organisation archivée avec succès : {0}." -#: src/user/mutations/close-account.js:92 -#: src/user/mutations/close-account.js:221 +#: src/user/mutations/close-account.js:96 +#: src/user/mutations/close-account.js:230 msgid "Successfully closed account." msgstr "Le compte a été fermé avec succès." -#: src/domain/mutations/request-scan.js:166 +#: src/domain/mutations/request-scan.js:175 msgid "Successfully dispatched one time scan." msgstr "Un seul balayage a été effectué avec succès." -#: src/domain/mutations/request-discovery.js:132 +#: src/domain/mutations/request-discovery.js:134 msgid "Successfully dispatched subdomain discovery scan." msgstr "L'analyse de découverte du sous-domaine a été effectuée avec succès." #: src/user/mutations/verify-account.js:97 -msgid "Successfully email verified account, and set TFA send method to email." -msgstr "Réussir à envoyer un email au compte vérifié, et définir la méthode d'envoi de la TFA sur email." +#~ msgid "Successfully email verified account, and set TFA send method to email." +#~ msgstr "Réussir à envoyer un email au compte vérifié, et définir la méthode d'envoi de la TFA sur email." + +#: src/user/mutations/verify-account.js:143 +msgid "Successfully email verified account." +msgstr "Envoi d'un courriel à un compte vérifié." -#: src/affiliation/mutations/invite-user-to-org.js:283 +#: src/affiliation/mutations/invite-user-to-org.js:282 msgid "Successfully invited user to organization, and sent notification email." msgstr "L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé." -#: src/affiliation/mutations/leave-organization.js:84 +#. placeholder {0}: org.slug +#: src/affiliation/mutations/leave-organization.js:86 msgid "Successfully left organization: {0}" msgstr "L'organisation a été quittée avec succès: {0}" -#: src/domain/mutations/unfavourite-domain.js:124 -msgid "Successfully removed domain: {0} from favourites." -msgstr "A réussi à supprimer le domaine : {0} des favoris." +#. placeholder {0}: org.slug +#: src/domain/mutations/remove-organizations-domains.js:477 +msgid "Successfully removed {domainCount} domain(s) from {0}." +msgstr "Supprimé avec succès le(s) domaine(s) {domainCount} de {0}." -#: src/domain/mutations/remove-domain.js:359 +#: src/domain/mutations/remove-organizations-domains.js:530 +#~ msgid "Successfully removed {domainCount} domains from {0}." +#~ msgstr "Suppression réussie des domaines {domainCount} de {0}." + +#. placeholder {0}: domain.domain +#. placeholder {1}: org.slug +#: src/domain/mutations/remove-domain.js:373 msgid "Successfully removed domain: {0} from {1}." msgstr "A réussi à supprimer le domaine : {0} de {1}." -#: src/organization/mutations/remove-organization.js:401 +#. placeholder {0}: domain.domain +#: src/domain/mutations/unfavourite-domain.js:126 +msgid "Successfully removed domain: {0} from favourites." +msgstr "A réussi à supprimer le domaine : {0} des favoris." + +#. placeholder {0}: organization.slug +#: src/organization/mutations/remove-organization.js:107 msgid "Successfully removed organization: {0}." msgstr "A réussi à supprimer l'organisation : {0}." -#: src/affiliation/mutations/remove-user-from-org.js:196 +#: src/affiliation/mutations/remove-user-from-org.js:201 msgid "Successfully removed user from organization." msgstr "L'utilisateur a été retiré de l'organisation avec succès." -#: src/domain/mutations/remove-organizations-domains.js:459 -msgid "Successfully removed {domainCount} domain(s) from {0}." -msgstr "Supprimé avec succès le(s) domaine(s) {domainCount} de {0}." - -#: src/domain/mutations/remove-organizations-domains.js:530 -#~ msgid "Successfully removed {domainCount} domains from {0}." -#~ msgstr "Suppression réussie des domaines {domainCount} de {0}." - -#: src/affiliation/mutations/request-org-affiliation.js:237 +#: src/affiliation/mutations/request-org-affiliation.js:231 msgid "Successfully requested invite to organization, and sent notification email." msgstr "La demande d'invitation à l'organisation a été effectuée avec succès et un courriel de notification a été envoyé." -#: src/affiliation/mutations/invite-user-to-org.js:174 +#: src/affiliation/mutations/invite-user-to-org.js:170 msgid "Successfully sent invitation to service, and organization email." msgstr "Envoi réussi de l'invitation au service, et de l'email de l'organisation." @@ -510,26 +711,50 @@ msgstr "Envoi réussi de l'invitation au service, et de l'email de l'organisatio msgid "Successfully signed out." msgstr "J'ai réussi à me déconnecter." -#: src/affiliation/mutations/transfer-org-ownership.js:185 +#. placeholder {0}: org.slug +#. placeholder {1}: requestedUser.userName +#: src/affiliation/mutations/transfer-org-ownership.js:188 msgid "Successfully transferred org: {0} ownership to user: {1}" msgstr "A réussi à transférer la propriété de org: {0} à l'utilisateur: {1}" -#: src/organization/mutations/verify-organization.js:135 +#. placeholder {0}: org.slug +#. placeholder {1}: tags.join(', ') +#: src/domain/mutations/update-domains-by-domain-ids.js:179 +#: src/domain/mutations/update-domains-by-filters.js:310 +msgid "Successfully updated {domainCount} domain(s) in {0} with {1}." +msgstr "Mise à jour réussie de {domainCount} domaine(s) dans {0} avec {1}." + +#. placeholder {0}: currentOrg.slug +#: src/organization/mutations/verify-organization.js:90 msgid "Successfully verified organization: {0}." msgstr "Envoi réussi de l'invitation au service, et de l'email de l'organisation." -#: src/user/mutations/verify-phone-number.js:89 +#: src/user/mutations/verify-phone-number.js:91 msgid "Successfully verified phone number, and set TFA send method to text." msgstr "Le numéro de téléphone a été vérifié avec succès, et la méthode d'envoi de la TFA a été réglée sur le texte." -#: src/user/mutations/authenticate.js:64 +#: src/tags/mutations/update-tag.js:113 +#~ msgid "Tag label already in use, please choose another and try again." +#~ msgstr "L'étiquette est déjà utilisée, veuillez en choisir une autre et réessayer." + +#: src/tags/mutations/create-tag.js:94 +#: src/tags/mutations/create-tag.js:148 +#: src/tags/mutations/update-tag.js:161 +msgid "Tag label already in use. Please try again with a different label." +msgstr "L'étiquette est déjà utilisée. Veuillez réessayer avec une autre étiquette." + +#: src/user/mutations/authenticate.js:66 msgid "Token value incorrect, please sign in again." msgstr "La valeur du jeton est incorrecte, veuillez vous connecter à nouveau." -#: src/user/mutations/sign-in.js:76 +#: src/user/mutations/sign-in.js:78 msgid "Too many failed login attempts, please reset your password, and try again." msgstr "Trop de tentatives de connexion ont échoué, veuillez réinitialiser votre mot de passe et réessayer." +#: src/user/mutations/complete-tour.js:73 +msgid "Tour completion confirmed successfully" +msgstr "Confirmation de l'achèvement du voyage" + #: src/user/mutations/verify-phone-number.js:51 msgid "Two factor code is incorrect. Please try again." msgstr "Le code à deux facteurs est incorrect. Veuillez réessayer." @@ -538,96 +763,116 @@ msgstr "Le code à deux facteurs est incorrect. Veuillez réessayer." msgid "Two factor code length is incorrect. Please try again." msgstr "La longueur du code à deux facteurs est incorrecte. Veuillez réessayer." -#: src/affiliation/mutations/leave-organization.js:70 -#: src/affiliation/mutations/leave-organization.js:77 +#: src/affiliation/mutations/leave-organization.js:71 +#: src/affiliation/mutations/leave-organization.js:79 msgid "Unable leave organization. Please try again." msgstr "Impossible de quitter l'organisation. Veuillez réessayer." -#: src/domain/mutations/add-organizations-domains.js:111 +#: src/domain/mutations/add-organizations-domains.js:108 msgid "Unable to add domains in unknown organization." msgstr "Impossible d'ajouter des domaines dans une organisation inconnue." -#: src/organization/mutations/archive-organization.js:101 -#: src/organization/mutations/archive-organization.js:111 -#: src/organization/mutations/archive-organization.js:129 -#: src/organization/mutations/archive-organization.js:146 -#: src/organization/mutations/archive-organization.js:155 +#: src/organization/data-source.js:179 +#: src/organization/data-source.js:194 +#: src/organization/data-source.js:209 +#: src/organization/data-source.js:217 msgid "Unable to archive organization. Please try again." msgstr "Impossible d'archiver l'organisation. Veuillez réessayer." -#: src/organization/mutations/archive-organization.js:54 +#: src/organization/mutations/archive-organization.js:52 msgid "Unable to archive unknown organization." msgstr "Impossible d'archiver une organisation inconnue." -#: src/user/mutations/authenticate.js:77 -#: src/user/mutations/authenticate.js:115 -#: src/user/mutations/authenticate.js:139 -#: src/user/mutations/authenticate.js:147 +#: src/user/mutations/authenticate.js:79 +#: src/user/mutations/authenticate.js:121 +#: src/user/mutations/authenticate.js:146 +#: src/user/mutations/authenticate.js:155 msgid "Unable to authenticate. Please try again." msgstr "Impossible de s'authentifier. Veuillez réessayer." -#: src/auth/check-permission.js:26 -#: src/auth/check-permission.js:64 -#: src/auth/check-super-admin.js:20 -#: src/auth/check-super-admin.js:30 +#: src/auth/checks/check-permission.js:26 +#: src/auth/checks/check-permission.js:64 +#: src/auth/checks/check-super-admin.js:20 +#: src/auth/checks/check-super-admin.js:30 +#: src/auth/loaders/load-permission-by-org-id.js:27 +#: src/auth/loaders/load-permission-by-org-id.js:73 msgid "Unable to check permission. Please try again." msgstr "Impossible de vérifier l'autorisation. Veuillez réessayer." -#: src/user/mutations/close-account.js:155 +#: src/user/mutations/close-account.js:160 msgid "Unable to close account of an undefined user." msgstr "Impossible de fermer le compte d'un utilisateur non défini." -#: src/user/mutations/close-account.js:48 -#: src/user/mutations/close-account.js:63 -#: src/user/mutations/close-account.js:70 -#: src/user/mutations/close-account.js:177 -#: src/user/mutations/close-account.js:192 +#: src/user/mutations/close-account.js:49 +#: src/user/mutations/close-account.js:65 +#: src/user/mutations/close-account.js:73 +#: src/user/mutations/close-account.js:183 #: src/user/mutations/close-account.js:199 +#: src/user/mutations/close-account.js:207 msgid "Unable to close account. Please try again." msgstr "Impossible de fermer le compte. Veuillez réessayer." -#: src/domain/mutations/create-domain.js:131 +#: src/user/mutations/complete-tour.js:39 +#: src/user/mutations/complete-tour.js:64 +msgid "Unable to confirm completion of the tour. Please try again." +msgstr "Impossible de confirmer l'achèvement de la visite. Veuillez réessayer." + +#: src/domain/mutations/create-domain.js:121 msgid "Unable to create domain in unknown organization." msgstr "Impossible de créer un domaine dans une organisation inconnue." -#: src/domain/mutations/create-domain.js:197 +#: src/domain/mutations/create-domain.js:216 msgid "Unable to create domain, organization has already claimed it." msgstr "Impossible de créer le domaine, l'organisation l'a déjà réclamé." -#: src/domain/mutations/create-domain.js:179 -#: src/domain/mutations/create-domain.js:187 -#: src/domain/mutations/create-domain.js:218 -#: src/domain/mutations/create-domain.js:226 -#: src/domain/mutations/create-domain.js:245 -#: src/domain/mutations/create-domain.js:266 -#: src/domain/mutations/create-domain.js:276 -#: src/domain/mutations/create-domain.js:294 -#: src/domain/mutations/create-domain.js:302 +#: src/domain/mutations/create-domain.js:198 +#: src/domain/mutations/create-domain.js:206 +#: src/domain/mutations/create-domain.js:238 +#: src/domain/mutations/create-domain.js:247 +#: src/domain/mutations/create-domain.js:267 +#: src/domain/mutations/create-domain.js:275 msgid "Unable to create domain. Please try again." msgstr "Impossible de créer un domaine. Veuillez réessayer." -#: src/domain/mutations/add-organizations-domains.js:283 +#: src/domain/mutations/add-organizations-domains.js:277 msgid "Unable to create domains. Please try again." msgstr "Impossible de créer des domaines. Veuillez réessayer." -#: src/organization/mutations/create-organization.js:182 -#: src/organization/mutations/create-organization.js:202 -#: src/organization/mutations/create-organization.js:211 +#: src/organization/data-source.js:58 +#: src/organization/data-source.js:76 +#: src/organization/data-source.js:84 msgid "Unable to create organization. Please try again." msgstr "Impossible de créer une organisation. Veuillez réessayer." -#: src/domain/mutations/request-discovery.js:85 +#: src/tags/mutations/create-tag.js:119 +msgid "Unable to create tag in unknown organization." +msgstr "Impossible de créer une étiquette dans une organisation inconnue." + +#: src/tags/mutations/create-tag.js:108 +msgid "Unable to create tag, tagId already in use." +msgstr "Impossible de créer une balise, tagId déjà utilisé." + +#: src/tags/data-source.js:58 +#: src/tags/data-source.js:65 +msgid "Unable to create tag. Please try again." +msgstr "Impossible de créer une balise. Veuillez réessayer." + +#: src/domain/mutations/request-discovery.js:86 msgid "Unable to discover domains for unknown organization." msgstr "Impossible de découvrir les domaines d'une organisation inconnue." +#: src/user/mutations/dismiss-message.js:39 +#: src/user/mutations/dismiss-message.js:66 +msgid "Unable to dismiss message. Please try again." +msgstr "Impossible de rejeter le message. Veuillez réessayer." + #: src/domain/mutations/request-scan.js:95 #: src/domain/mutations/request-scan.js:109 #: src/domain/mutations/request-scan.js:123 #~ msgid "Unable to dispatch one time scan. Please try again." #~ msgstr "Impossible d'envoyer un scan unique. Veuillez réessayer." -#: src/organization/objects/organization.js:194 -#: src/organization/objects/organization.js:204 +#: src/organization/objects/organization.js:265 msgid "Unable to export organization. Please try again." msgstr "Impossible d'exporter l'organisation. Veuillez réessayer." @@ -637,8 +882,8 @@ msgstr "Impossible de favoriser le domaine, l'utilisateur l'a déjà favorisé." #: src/domain/mutations/favourite-domain.js:66 #: src/domain/mutations/favourite-domain.js:74 -#: src/domain/mutations/favourite-domain.js:102 -#: src/domain/mutations/favourite-domain.js:109 +#: src/domain/mutations/favourite-domain.js:103 +#: src/domain/mutations/favourite-domain.js:111 #: src/domain/mutations/unfavourite-domain.js:68 #: src/domain/mutations/unfavourite-domain.js:76 msgid "Unable to favourite domain. Please try again." @@ -688,6 +933,11 @@ msgstr "Impossible de trouver les données de synthèse DMARC. Veuillez réessay msgid "Unable to find DNS scan(s). Please try again." msgstr "Impossible de trouver le(s) scan(s) DNS. Veuillez réessayer." +#: src/guidance-tag/loaders/load-guidance-tags.js:26 +#: src/guidance-tag/loaders/load-guidance-tags.js:36 +msgid "Unable to find guidance tag(s). Please try again." +msgstr "Impossible de trouver les étiquettes d'orientation. Veuillez réessayer." + #: src/guidance-tag/loaders/load-https-guidance-tags.js:33 #: src/guidance-tag/loaders/load-https-guidance-tags.js:47 msgid "Unable to find HTTPS guidance tag(s). Please try again." @@ -733,26 +983,33 @@ msgstr "Impossible de trouver l'affiliation de l'utilisateur (s). Veuillez rées msgid "Unable to find verified organization(s). Please try again." msgstr "Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer." -#: src/affiliation/mutations/invite-user-to-org.js:119 -#: src/affiliation/mutations/invite-user-to-org.js:129 -#: src/affiliation/mutations/invite-user-to-org.js:194 +#: src/domain/mutations/ignore-cve.js:64 +#: src/domain/mutations/ignore-cve.js:101 +#: src/domain/mutations/ignore-cve.js:123 +#: src/domain/mutations/ignore-cve.js:138 +#: src/domain/mutations/ignore-cve.js:149 +msgid "Unable to ignore CVE. Please try again." +msgstr "Impossible d'ignorer le CVE. Veuillez réessayer." + +#: src/affiliation/mutations/invite-user-to-org.js:124 +#: src/affiliation/mutations/invite-user-to-org.js:190 msgid "Unable to invite user to organization. Please try again." msgstr "Impossible d'inviter un utilisateur dans une organisation. Veuillez réessayer." -#: src/affiliation/mutations/invite-user-to-org.js:206 +#: src/affiliation/mutations/invite-user-to-org.js:202 msgid "Unable to invite user to organization. User is already affiliated with organization." msgstr "Impossible d'inviter un utilisateur dans une organisation. L'utilisateur est déjà affilié à l'organisation." -#: src/affiliation/mutations/invite-user-to-org.js:82 +#: src/affiliation/mutations/invite-user-to-org.js:84 msgid "Unable to invite user to unknown organization." msgstr "Impossible d'inviter un utilisateur à une organisation inconnue." -#: src/affiliation/mutations/invite-user-to-org.js:235 -#: src/affiliation/mutations/invite-user-to-org.js:255 +#: src/affiliation/mutations/invite-user-to-org.js:232 +#: src/affiliation/mutations/invite-user-to-org.js:253 msgid "Unable to invite user. Please try again." msgstr "Impossible d'inviter un utilisateur. Veuillez réessayer." -#: src/affiliation/mutations/invite-user-to-org.js:68 +#: src/affiliation/mutations/invite-user-to-org.js:70 msgid "Unable to invite yourself to an org." msgstr "Impossible de s'inviter à un org." @@ -765,11 +1022,34 @@ msgstr "Impossible de s'inviter à un org." msgid "Unable to leave undefined organization." msgstr "Impossible de quitter une organisation non définie." +#: src/additional-findings/loaders/load-additional-findings-by-domain-id.js:24 +#: src/additional-findings/loaders/load-additional-findings-by-domain-id.js:34 +msgid "Unable to load additional findings. Please try again." +msgstr "Impossible de charger des résultats supplémentaires. Veuillez réessayer." + +#: src/auth/checks/check-user-belongs-to-org.js:20 +msgid "Unable to load affiliation information. Please try again." +msgstr "Impossible de charger les informations d'affiliation. Veuillez réessayer." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:303 +#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:449 +msgid "Unable to load affiliation(s). Please try again." +msgstr "Impossible de charger l'affiliation (s). Veuillez réessayer." + #: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:254 #: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:266 msgid "Unable to load Aggregate guidance tag(s). Please try again." msgstr "Impossible de charger le(s) tag(s) d'orientation des agrégats. Veuillez réessayer." +#: src/organization/loaders/load-all-organization-domain-statuses.js:57 +#~ msgid "Unable to load all organization domain statuses. Please try again." +#~ msgstr "Impossible de charger tous les statuts de domaine d'organisation. Veuillez réessayer." + +#: src/summaries/loaders/load-chart-summaries-by-period.js:50 +#: src/summaries/loaders/load-chart-summaries-by-period.js:60 +msgid "Unable to load chart summary data. Please try again." +msgstr "Impossible de charger les données du résumé du graphique. Veuillez réessayer." + #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:141 #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:153 @@ -792,8 +1072,8 @@ msgstr "Impossible de charger le(s) tag(s) d'orientation DKIM. Veuillez réessay #~ msgstr "Impossible de charger le(s) scan(s) DKIM. Veuillez réessayer." #: src/summaries/queries/dkim-summary.js:12 -msgid "Unable to load DKIM summary. Please try again." -msgstr "Impossible de charger le résumé DKIM. Veuillez réessayer." +#~ msgid "Unable to load DKIM summary. Please try again." +#~ msgstr "Impossible de charger le résumé DKIM. Veuillez réessayer." #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:141 @@ -806,17 +1086,17 @@ msgstr "Impossible de charger les données d'échec DMARC. Veuillez réessayer." msgid "Unable to load DMARC guidance tag(s). Please try again." msgstr "Impossible de charger le(s) tag(s) d'orientation DMARC. Veuillez réessayer." -#: src/summaries/queries/dmarc-phase-summary.js:14 -msgid "Unable to load DMARC phase summary. Please try again." -msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." +#: src/summaries/queries/dmarc-phase-summary.js:12 +#~ msgid "Unable to load DMARC phase summary. Please try again." +#~ msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:319 #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:331 #~ msgid "Unable to load DMARC scan(s). Please try again." #~ msgstr "Impossible de charger le(s) scan(s) DMARC. Veuillez réessayer." -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:446 -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:456 +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:449 +#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:459 #: src/dmarc-summaries/loaders/load-dmarc-sum-edge-by-domain-id-period.js:20 #: src/dmarc-summaries/loaders/load-dmarc-sum-edge-by-domain-id-period.js:32 #: src/dmarc-summaries/loaders/load-yearly-dmarc-sum-edges.js:20 @@ -825,16 +1105,44 @@ msgid "Unable to load DMARC summary data. Please try again." msgstr "Impossible de charger les données de synthèse DMARC. Veuillez réessayer." #: src/summaries/queries/dmarc-summary.js:12 -msgid "Unable to load DMARC summary. Please try again." -msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." +#~ msgid "Unable to load DMARC summary. Please try again." +#~ msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:154 #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:164 -#: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:156 -#: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:166 msgid "Unable to load DNS scan(s). Please try again." msgstr "Impossible de charger le(s) scan(s) DNS. Veuillez réessayer." +#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:18 +#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:28 +msgid "Unable to load domain selector(s). Please try again." +msgstr "Impossible de charger le(s) sélecteur(s) de domaine. Veuillez réessayer." + +#: src/domain/loaders/load-domain-by-domain.js:19 +#: src/domain/loaders/load-domain-by-domain.js:31 +#: src/domain/loaders/load-domain-by-key.js:19 +#: src/domain/loaders/load-domain-by-key.js:31 +msgid "Unable to load domain. Please try again." +msgstr "Impossible de charger le domaine. Veuillez réessayer." + +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:402 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:412 +#: src/domain/loaders/load-domain-connections-by-user-id.js:469 +#: src/user/loaders/load-my-tracker-by-user-id.js:33 +msgid "Unable to load domain(s). Please try again." +msgstr "Impossible de charger le(s) domaine(s). Veuillez réessayer." + +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:13 +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:140 +#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:152 +msgid "Unable to load full pass data. Please try again." +msgstr "Impossible de charger les données complètes de la passe. Veuillez réessayer." + +#: src/guidance-tag/loaders/load-guidance-tags-connections.js:54 +#: src/guidance-tag/loaders/load-guidance-tags-connections.js:64 +msgid "Unable to load guidance tag(s). Please try again." +msgstr "Impossible de charger le(s) tag(s) d'orientation. Veuillez réessayer." + #: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:260 #: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:272 msgid "Unable to load HTTPS guidance tag(s). Please try again." @@ -847,8 +1155,62 @@ msgstr "Impossible de charger la ou les balises d'orientation HTTPS. Veuillez r #~ msgstr "Impossible de charger le(s) scan(s) HTTPS. Veuillez réessayer." #: src/summaries/queries/https-summary.js:13 -msgid "Unable to load HTTPS summary. Please try again." -msgstr "Impossible de charger le résumé HTTPS. Veuillez réessayer." +#~ msgid "Unable to load HTTPS summary. Please try again." +#~ msgstr "Impossible de charger le résumé HTTPS. Veuillez réessayer." + +#: src/audit-logs/loaders/load-audit-log-by-key.js:19 +#: src/audit-logs/loaders/load-audit-log-by-key.js:31 +msgid "Unable to load log. Please try again." +msgstr "Impossible de charger le journal. Veuillez réessayer." + +#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:318 +msgid "Unable to load log(s). Please try again." +msgstr "Impossible de charger le(s) journal(s). Veuillez réessayer." + +#: src/summaries/queries/mail-summary.js:12 +#~ msgid "Unable to load mail summary. Please try again." +#~ msgstr "Impossible de charger le résumé du courrier. Veuillez réessayer." + +#: src/additional-findings/loaders/load-top-25-reports.js:29 +#: src/organization/loaders/load-all-organization-domain-statuses.js:164 +#: src/organization/loaders/load-organization-domain-statuses.js:172 +msgid "Unable to load organization domain statuses. Please try again." +msgstr "Impossible de charger les statuts des domaines d'organisation. Veuillez réessayer." + +#: src/organization/loaders/load-organization-names-by-id.js:19 +#: src/organization/loaders/load-organization-names-by-id.js:29 +msgid "Unable to load organization names. Please try again." +msgstr "Impossible de charger les noms des organisations. Veuillez réessayer." + +#: src/organization/loaders/load-organization-summaries-by-period.js:56 +#: src/organization/loaders/load-organization-summaries-by-period.js:66 +msgid "Unable to load organization summary data. Please try again." +msgstr "Impossible de charger les données de synthèse de l'organisation. Veuillez réessayer." + +#: src/organization/data-source.js:117 +#: src/organization/data-source.js:124 +#: src/organization/data-source.js:144 +#: src/organization/data-source.js:152 +msgid "Unable to load organization. Please try again." +msgstr "Impossible de charger l'organisation. Veuillez réessayer." + +#: src/organization/loaders/load-organization-by-key.js:31 +#: src/organization/loaders/load-organization-by-key.js:41 +#: src/organization/loaders/load-organization-by-slug.js:34 +#: src/organization/loaders/load-organization-by-slug.js:45 +#: src/organization/loaders/load-organization-connections-by-domain-id.js:508 +#: src/organization/loaders/load-organization-connections-by-domain-id.js:518 +#: src/organization/loaders/load-organization-connections-by-user-id.js:544 +#: src/organization/loaders/load-organization-connections-by-user-id.js:554 +msgid "Unable to load organization(s). Please try again." +msgstr "Impossible de charger l'organisation (s). Veuillez réessayer." + +#: src/auth/checks/check-org-owner.js:19 +#: src/auth/checks/check-org-owner.js:27 +#: src/auth/loaders/load-org-owner-by-org-id.js:23 +#: src/auth/loaders/load-org-owner-by-org-id.js:33 +msgid "Unable to load owner information. Please try again." +msgstr "Impossible de charger les informations sur le propriétaire. Veuillez réessayer." #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:140 @@ -867,8 +1229,8 @@ msgstr "Impossible de charger le(s) tag(s) d'orientation SPF. Veuillez réessaye #~ msgstr "Impossible de charger le(s) scan(s) SPF. Veuillez réessayer." #: src/summaries/queries/spf-summary.js:12 -msgid "Unable to load SPF summary. Please try again." -msgstr "Impossible de charger le résumé SPF. Veuillez réessayer." +#~ msgid "Unable to load SPF summary. Please try again." +#~ msgstr "Impossible de charger le résumé SPF. Veuillez réessayer." #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:260 #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:272 @@ -881,105 +1243,32 @@ msgstr "Impossible de charger le(s) tag(s) d'orientation SSL. Veuillez réessaye #~ msgstr "Impossible de charger le(s) scan(s) SSL. Veuillez réessayer." #: src/summaries/queries/ssl-summary.js:12 -msgid "Unable to load SSL summary. Please try again." -msgstr "Impossible de charger le résumé SSL. Veuillez réessayer." - -#: src/auth/check-user-belongs-to-org.js:20 -msgid "Unable to load affiliation information. Please try again." -msgstr "Impossible de charger les informations d'affiliation. Veuillez réessayer." - -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:259 -#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:449 -msgid "Unable to load affiliation(s). Please try again." -msgstr "Impossible de charger l'affiliation (s). Veuillez réessayer." - -#: src/organization/loaders/load-all-organization-domain-statuses.js:57 -msgid "Unable to load all organization domain statuses. Please try again." -msgstr "Impossible de charger tous les statuts de domaine d'organisation. Veuillez réessayer." - -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:122 -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:132 -msgid "Unable to load chart summary data. Please try again." -msgstr "Impossible de charger les données du résumé du graphique. Veuillez réessayer." - -#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:18 -#: src/domain/loaders/load-dkim-selectors-by-domain-id.js:28 -msgid "Unable to load domain selector(s). Please try again." -msgstr "Impossible de charger le(s) sélecteur(s) de domaine. Veuillez réessayer." - -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:554 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:564 -#: src/domain/loaders/load-domain-connections-by-user-id.js:452 -#: src/domain/loaders/load-domain-tags-by-org-id.js:27 -#: src/user/loaders/load-my-tracker-by-user-id.js:33 -msgid "Unable to load domain(s). Please try again." -msgstr "Impossible de charger le(s) domaine(s). Veuillez réessayer." - -#: src/domain/loaders/load-domain-by-domain.js:19 -#: src/domain/loaders/load-domain-by-domain.js:31 -#: src/domain/loaders/load-domain-by-key.js:19 -#: src/domain/loaders/load-domain-by-key.js:31 -msgid "Unable to load domain. Please try again." -msgstr "Impossible de charger le domaine. Veuillez réessayer." - -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:13 -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:140 -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:152 -msgid "Unable to load full pass data. Please try again." -msgstr "Impossible de charger les données complètes de la passe. Veuillez réessayer." - -#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:318 -msgid "Unable to load log(s). Please try again." -msgstr "Impossible de charger le(s) journal(s). Veuillez réessayer." - -#: src/audit-logs/loaders/load-audit-log-by-key.js:19 -#: src/audit-logs/loaders/load-audit-log-by-key.js:31 -msgid "Unable to load log. Please try again." -msgstr "Impossible de charger le journal. Veuillez réessayer." - -#: src/summaries/queries/mail-summary.js:12 -msgid "Unable to load mail summary. Please try again." -msgstr "Impossible de charger le résumé du courrier. Veuillez réessayer." - -#: src/organization/loaders/load-organization-domain-statuses.js:118 -msgid "Unable to load organization domain statuses. Please try again." -msgstr "Impossible de charger les statuts des domaines d'organisation. Veuillez réessayer." - -#: src/organization/loaders/load-organization-summaries-by-period.js:108 -#: src/organization/loaders/load-organization-summaries-by-period.js:118 -msgid "Unable to load organization summary data. Please try again." -msgstr "Impossible de charger les données de synthèse de l'organisation. Veuillez réessayer." - -#: src/organization/loaders/load-organization-by-key.js:33 -#: src/organization/loaders/load-organization-by-key.js:47 -#: src/organization/loaders/load-organization-by-slug.js:36 -#: src/organization/loaders/load-organization-by-slug.js:51 -#: src/organization/loaders/load-organization-connections-by-domain-id.js:499 -#: src/organization/loaders/load-organization-connections-by-domain-id.js:509 -#: src/organization/loaders/load-organization-connections-by-user-id.js:505 -#: src/organization/loaders/load-organization-connections-by-user-id.js:515 -msgid "Unable to load organization(s). Please try again." -msgstr "Impossible de charger l'organisation (s). Veuillez réessayer." - -#: src/auth/check-org-owner.js:19 -#: src/auth/check-org-owner.js:27 -msgid "Unable to load owner information. Please try again." -msgstr "Impossible de charger les informations sur le propriétaire. Veuillez réessayer." +#~ msgid "Unable to load SSL summary. Please try again." +#~ msgstr "Impossible de charger le résumé SSL. Veuillez réessayer." #: src/summaries/loaders/load-chart-summary-by-key.js:17 #: src/summaries/loaders/load-chart-summary-by-key.js:25 -msgid "Unable to load summary. Please try again." -msgstr "Impossible de charger le résumé. Veuillez réessayer." +#~ msgid "Unable to load summary. Please try again." +#~ msgstr "Impossible de charger le résumé. Veuillez réessayer." + +#: src/tags/loaders/load-all-tags.js:36 +#: src/tags/loaders/load-all-tags.js:44 +#: src/tags/loaders/load-tag-by-tag-id.js:25 +#: src/tags/loaders/load-tag-by-tag-id.js:35 +#: src/tags/loaders/load-tags-by-org.js:36 +#: src/tags/loaders/load-tags-by-org.js:44 +msgid "Unable to load tag(s). Please try again." +msgstr "Impossible de charger le(s) tag(s). Veuillez réessayer." #: src/domain/loaders/load-domain-tags-by-org-id.js:37 -msgid "Unable to load tags(s). Please try again." -msgstr "Impossible de charger les balises. Veuillez réessayer." +#~ msgid "Unable to load tags(s). Please try again." +#~ msgstr "Impossible de charger les balises. Veuillez réessayer." #: src/user/loaders/load-user-by-key.js:19 #: src/user/loaders/load-user-by-key.js:31 #: src/user/loaders/load-user-by-username.js:19 #: src/user/loaders/load-user-by-username.js:31 -#: src/user/loaders/load-user-connections-by-user-id.js:345 +#: src/user/loaders/load-user-connections-by-user-id.js:346 msgid "Unable to load user(s). Please try again." msgstr "Impossible de charger le(s) utilisateur(s). Veuillez réessayer." @@ -1001,9 +1290,13 @@ msgstr "Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer msgid "Unable to load verified organization(s). Please try again." msgstr "Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer." +#: src/dmarc-summaries/loaders/load-all-verified-rua-domains.js:27 +msgid "Unable to load verified rua domains. Please try again." +msgstr "Impossible de charger les domaines rua vérifiés. Veuillez réessayer." + #: src/summaries/queries/web-connections-summary.js:12 -msgid "Unable to load web connections summary. Please try again." -msgstr "Impossible de charger le résumé des connexions web. Veuillez réessayer." +#~ msgid "Unable to load web connections summary. Please try again." +#~ msgstr "Impossible de charger le résumé des connexions web. Veuillez réessayer." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:169 #: src/web-scan/loaders/load-web-connections-by-domain-id.js:179 @@ -1014,15 +1307,15 @@ msgid "Unable to load web scan(s). Please try again." msgstr "Impossible de charger le(s) scan(s) web. Veuillez réessayer." #: src/summaries/queries/web-summary.js:13 -msgid "Unable to load web summary. Please try again." -msgstr "Impossible de charger le résumé web. Veuillez réessayer." +#~ msgid "Unable to load web summary. Please try again." +#~ msgstr "Impossible de charger le résumé web. Veuillez réessayer." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:249 +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:293 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:437 msgid "Unable to query affiliation(s). Please try again." msgstr "Impossible de demander l'affiliation (s). Veuillez réessayer." -#: src/domain/loaders/load-domain-connections-by-user-id.js:442 +#: src/domain/loaders/load-domain-connections-by-user-id.js:459 #: src/user/loaders/load-my-tracker-by-user-id.js:23 msgid "Unable to query domain(s). Please try again." msgstr "Impossible d'interroger le(s) domaine(s). Veuillez réessayer." @@ -1031,80 +1324,78 @@ msgstr "Impossible d'interroger le(s) domaine(s). Veuillez réessayer." msgid "Unable to query log(s). Please try again." msgstr "Impossible d'interroger le(s) journal(s). Veuillez réessayer." -#: src/user/loaders/load-user-connections-by-user-id.js:335 +#: src/user/loaders/load-user-connections-by-user-id.js:336 msgid "Unable to query user(s). Please try again." msgstr "Impossible d'interroger le(s) utilisateur(s). Veuillez réessayer." -#: src/user/mutations/refresh-tokens.js:49 -#: src/user/mutations/refresh-tokens.js:63 -#: src/user/mutations/refresh-tokens.js:78 -#: src/user/mutations/refresh-tokens.js:93 -#: src/user/mutations/refresh-tokens.js:105 -#: src/user/mutations/refresh-tokens.js:136 -#: src/user/mutations/refresh-tokens.js:145 +#: src/user/mutations/refresh-tokens.js:47 +#: src/user/mutations/refresh-tokens.js:59 +#: src/user/mutations/refresh-tokens.js:72 +#: src/user/mutations/refresh-tokens.js:85 +#: src/user/mutations/refresh-tokens.js:95 +#: src/user/mutations/refresh-tokens.js:123 +#: src/user/mutations/refresh-tokens.js:131 msgid "Unable to refresh tokens, please sign in." msgstr "Impossible de rafraîchir les jetons, veuillez vous connecter." -#: src/affiliation/mutations/remove-user-from-org.js:104 +#: src/affiliation/mutations/remove-user-from-org.js:106 msgid "Unable to remove a user that already does not belong to this organization." msgstr "Impossible de supprimer un utilisateur qui n'appartient déjà plus à cette organisation." -#: src/domain/mutations/remove-domain.js:80 +#: src/domain/mutations/remove-domain.js:82 msgid "Unable to remove domain from unknown organization." msgstr "Impossible de supprimer le domaine d'une organisation inconnue." -#: src/domain/mutations/remove-domain.js:136 +#: src/domain/mutations/remove-domain.js:138 msgid "Unable to remove domain. Domain is not part of organization." msgstr "Impossible de supprimer le domaine. Le domaine ne fait pas partie de l'organisation." -#: src/domain/mutations/remove-domain.js:123 -#: src/domain/mutations/remove-domain.js:152 -#: src/domain/mutations/remove-domain.js:185 -#: src/domain/mutations/remove-domain.js:204 -#: src/domain/mutations/remove-domain.js:230 -#: src/domain/mutations/remove-domain.js:248 -#: src/domain/mutations/remove-domain.js:265 -#: src/domain/mutations/remove-domain.js:281 -#: src/domain/mutations/remove-domain.js:298 -#: src/domain/mutations/remove-domain.js:321 -#: src/domain/mutations/remove-domain.js:332 +#: src/domain/mutations/remove-domain.js:125 +#: src/domain/mutations/remove-domain.js:154 +#: src/domain/mutations/remove-domain.js:188 +#: src/domain/mutations/remove-domain.js:208 +#: src/domain/mutations/remove-domain.js:237 +#: src/domain/mutations/remove-domain.js:256 +#: src/domain/mutations/remove-domain.js:274 +#: src/domain/mutations/remove-domain.js:291 +#: src/domain/mutations/remove-domain.js:309 +#: src/domain/mutations/remove-domain.js:333 +#: src/domain/mutations/remove-domain.js:345 msgid "Unable to remove domain. Please try again." msgstr "Impossible de supprimer le domaine. Veuillez réessayer." -#: src/domain/mutations/remove-organizations-domains.js:90 +#: src/domain/mutations/remove-organizations-domains.js:91 msgid "Unable to remove domains from unknown organization." msgstr "Impossible de supprimer les domaines d'une organisation inconnue." -#: src/organization/mutations/remove-organization.js:101 -#: src/organization/mutations/remove-organization.js:111 -#: src/organization/mutations/remove-organization.js:141 -#: src/organization/mutations/remove-organization.js:156 -#: src/organization/mutations/remove-organization.js:186 -#: src/organization/mutations/remove-organization.js:196 -#: src/organization/mutations/remove-organization.js:220 -#: src/organization/mutations/remove-organization.js:238 -#: src/organization/mutations/remove-organization.js:255 -#: src/organization/mutations/remove-organization.js:271 -#: src/organization/mutations/remove-organization.js:303 -#: src/organization/mutations/remove-organization.js:367 -#: src/organization/mutations/remove-organization.js:376 +#: src/organization/data-source.js:278 +#: src/organization/data-source.js:307 +#: src/organization/data-source.js:321 +#: src/organization/data-source.js:350 +#: src/organization/data-source.js:374 +#: src/organization/data-source.js:390 +#: src/organization/data-source.js:405 +#: src/organization/data-source.js:419 +#: src/organization/data-source.js:448 +#: src/organization/data-source.js:490 +#: src/organization/data-source.js:498 msgid "Unable to remove organization. Please try again." msgstr "Impossible de supprimer l'organisation. Veuillez réessayer." -#: src/user/mutations/remove-phone-number.js:57 -#: src/user/mutations/remove-phone-number.js:68 +#: src/user/mutations/remove-phone-number.js:51 +#: src/user/mutations/remove-phone-number.js:59 msgid "Unable to remove phone number. Please try again." msgstr "Impossible de supprimer le numéro de téléphone. Veuillez réessayer." -#: src/domain/mutations/remove-domain.js:65 +#: src/domain/mutations/remove-domain.js:67 msgid "Unable to remove unknown domain." msgstr "Impossible de supprimer un domaine inconnu." -#: src/organization/mutations/remove-organization.js:54 +#: src/organization/mutations/remove-organization.js:52 msgid "Unable to remove unknown organization." msgstr "Impossible de supprimer une organisation inconnue." -#: src/affiliation/mutations/remove-user-from-org.js:77 +#: src/affiliation/mutations/remove-user-from-org.js:79 msgid "Unable to remove unknown user from organization." msgstr "Impossible de supprimer un utilisateur inconnu de l'organisation." @@ -1112,71 +1403,70 @@ msgstr "Impossible de supprimer un utilisateur inconnu de l'organisation." #~ msgid "Unable to remove user from organization." #~ msgstr "Impossible de supprimer un utilisateur de l'organisation." -#: src/affiliation/mutations/remove-user-from-org.js:94 -#: src/affiliation/mutations/remove-user-from-org.js:115 -#: src/affiliation/mutations/remove-user-from-org.js:161 -#: src/affiliation/mutations/remove-user-from-org.js:170 +#: src/affiliation/mutations/remove-user-from-org.js:96 +#: src/affiliation/mutations/remove-user-from-org.js:117 +#: src/affiliation/mutations/remove-user-from-org.js:164 +#: src/affiliation/mutations/remove-user-from-org.js:174 msgid "Unable to remove user from this organization. Please try again." msgstr "Impossible de supprimer l'utilisateur de cette organisation. Veuillez réessayer." -#: src/affiliation/mutations/remove-user-from-org.js:61 +#: src/affiliation/mutations/remove-user-from-org.js:63 msgid "Unable to remove user from unknown organization." msgstr "Impossible de supprimer un utilisateur d'une organisation inconnue." -#: src/domain/mutations/request-scan.js:117 +#: src/domain/mutations/request-scan.js:120 msgid "Unable to request a one time scan on a domain that already has a pending scan." msgstr "Impossible de demander une analyse unique sur un domaine qui a déjà une analyse en cours." -#: src/domain/mutations/request-scan.js:52 +#: src/domain/mutations/request-scan.js:55 msgid "Unable to request a one time scan on an unknown domain." msgstr "Impossible de demander un scan unique sur un domaine inconnu." -#: src/domain/mutations/request-scan.js:125 +#: src/domain/mutations/request-scan.js:128 msgid "Unable to request a one time scan. Please try again." msgstr "Impossible de demander une analyse unique. Veuillez réessayer." -#: src/domain/mutations/request-discovery.js:62 +#: src/domain/mutations/request-discovery.js:63 msgid "Unable to request a subdomain discovery scan on an invalid domain." msgstr "Impossible de demander un scan de découverte de sous-domaine sur un domaine invalide." -#: src/domain/mutations/request-discovery.js:72 +#: src/domain/mutations/request-discovery.js:73 msgid "Unable to request a subdomain discovery scan on an unknown domain." msgstr "Impossible de demander une analyse de découverte de sous-domaine sur un domaine inconnu." -#: src/affiliation/mutations/request-org-affiliation.js:97 +#: src/affiliation/mutations/request-org-affiliation.js:98 msgid "Unable to request invite to organization with which you are already affiliated." msgstr "Impossible de demander une invitation à une organisation à laquelle vous êtes déjà affilié." -#: src/affiliation/mutations/request-org-affiliation.js:87 +#: src/affiliation/mutations/request-org-affiliation.js:88 msgid "Unable to request invite to organization with which you have already requested to join." msgstr "Impossible de demander une invitation à une organisation à laquelle vous avez déjà demandé à adhérer." -#: src/affiliation/mutations/request-org-affiliation.js:58 +#: src/affiliation/mutations/request-org-affiliation.js:59 msgid "Unable to request invite to unknown organization." msgstr "Impossible de demander une invitation à une organisation inconnue." -#: src/affiliation/mutations/request-org-affiliation.js:74 -#: src/affiliation/mutations/request-org-affiliation.js:122 -#: src/affiliation/mutations/request-org-affiliation.js:138 -#: src/affiliation/mutations/request-org-affiliation.js:148 -#: src/affiliation/mutations/request-org-affiliation.js:174 -#: src/affiliation/mutations/request-org-affiliation.js:183 -#: src/affiliation/mutations/request-org-affiliation.js:205 +#: src/affiliation/mutations/request-org-affiliation.js:75 +#: src/affiliation/mutations/request-org-affiliation.js:124 +#: src/affiliation/mutations/request-org-affiliation.js:141 +#: src/affiliation/mutations/request-org-affiliation.js:152 +#: src/affiliation/mutations/request-org-affiliation.js:167 +#: src/affiliation/mutations/request-org-affiliation.js:198 msgid "Unable to request invite. Please try again." msgstr "Impossible de demander une invitation. Veuillez réessayer." #: src/user/mutations/reset-password.js:86 -msgid "Unable to reset password. Please request a new email." -msgstr "Impossible de réinitialiser le mot de passe. Veuillez demander un nouvel e-mail." +#~ msgid "Unable to reset password. Please request a new email." +#~ msgstr "Impossible de réinitialiser le mot de passe. Veuillez demander un nouvel e-mail." -#: src/user/mutations/reset-password.js:74 -#: src/user/mutations/reset-password.js:134 -#: src/user/mutations/reset-password.js:141 +#: src/user/mutations/reset-password.js:76 +#: src/user/mutations/reset-password.js:125 +#: src/user/mutations/reset-password.js:133 msgid "Unable to reset password. Please try again." msgstr "Impossible de réinitialiser le mot de passe. Veuillez réessayer." -#: src/domain/objects/domain.js:290 -#: src/domain/objects/domain.js:325 +#: src/domain/objects/domain.js:274 +#: src/domain/objects/domain.js:309 msgid "Unable to retrieve DMARC report information for: {domain}" msgstr "Impossible de récupérer les informations du rapport DMARC pour : {domain}" @@ -1206,45 +1496,62 @@ msgstr "Impossible d'envoyer l'email de demande d'invitation à l'org. Veuillez msgid "Unable to send password reset email. Please try again." msgstr "Impossible d'envoyer l'email de réinitialisation du mot de passe. Veuillez réessayer." +#: src/notify/notify-send-role-change-email.js:21 +#~ msgid "Unable to send role update email. Please try again." +#~ msgstr "Impossible d'envoyer l'e-mail de mise à jour du rôle. Veuillez réessayer." + #: src/notify/notify-send-tfa-text-msg.js:30 -msgid "Unable to send two factor authentication message. Please try again." -msgstr "Impossible d'envoyer le message d'authentification à deux facteurs. Veuillez réessayer." +#~ msgid "Unable to send two factor authentication message. Please try again." +#~ msgstr "Impossible d'envoyer le message d'authentification à deux facteurs. Veuillez réessayer." + +#: src/notify/notify-send-updated-username-email.js:18 +#: src/user/mutations/verify-account.js:102 +msgid "Unable to send updated username email. Please try again." +msgstr "Impossible d'envoyer l'email de mise à jour du nom d'utilisateur. Veuillez réessayer." -#: src/notify/notify-send-verification-email.js:30 +#: src/notify/notify-send-verification-email.js:18 msgid "Unable to send verification email. Please try again." msgstr "Impossible d'envoyer l'email de vérification. Veuillez réessayer." -#: src/user/mutations/set-phone-number.js:96 -#: src/user/mutations/set-phone-number.js:103 +#: src/user/mutations/set-phone-number.js:97 +#: src/user/mutations/set-phone-number.js:105 msgid "Unable to set phone number, please try again." msgstr "Impossible de définir le numéro de téléphone, veuillez réessayer." -#: src/user/mutations/sign-in.js:95 -#: src/user/mutations/sign-in.js:128 -#: src/user/mutations/sign-in.js:135 -#: src/user/mutations/sign-in.js:179 -#: src/user/mutations/sign-in.js:186 -#: src/user/mutations/sign-in.js:246 -#: src/user/mutations/sign-in.js:253 +#: src/user/mutations/sign-in.js:98 +#: src/user/mutations/sign-in.js:132 +#: src/user/mutations/sign-in.js:140 +#: src/user/mutations/sign-in.js:198 +#: src/user/mutations/sign-in.js:206 +#: src/user/mutations/sign-in.js:270 +#: src/user/mutations/sign-in.js:278 msgid "Unable to sign in, please try again." msgstr "Impossible de se connecter, veuillez réessayer." -#: src/user/mutations/sign-up.js:183 -#: src/user/mutations/sign-up.js:193 +#: src/user/mutations/sign-up.js:197 +#: src/user/mutations/sign-up.js:208 msgid "Unable to sign up, please contact org admin for a new invite." msgstr "Impossible de s'inscrire, veuillez contacter l'administrateur de l'organisation pour obtenir une nouvelle invitation." -#: src/user/mutations/sign-up.js:156 -#: src/user/mutations/sign-up.js:164 -#: src/user/mutations/sign-up.js:213 -#: src/user/mutations/sign-up.js:221 +#: src/user/mutations/sign-up.js:168 +#: src/user/mutations/sign-up.js:177 +#: src/user/mutations/sign-up.js:229 +#: src/user/mutations/sign-up.js:238 msgid "Unable to sign up. Please try again." msgstr "Impossible de s'inscrire. Veuillez réessayer." +#: src/domain/mutations/unignore-cve.js:64 +#: src/domain/mutations/unignore-cve.js:101 +#: src/domain/mutations/unignore-cve.js:123 +#: src/domain/mutations/unignore-cve.js:138 +#: src/domain/mutations/unignore-cve.js:149 +msgid "Unable to stop ignoring CVE. Please try again." +msgstr "Impossible d'arrêter d'ignorer le CVE. Veuillez réessayer." + #: src/affiliation/mutations/transfer-org-ownership.js:106 -#: src/affiliation/mutations/transfer-org-ownership.js:145 -#: src/affiliation/mutations/transfer-org-ownership.js:167 -#: src/affiliation/mutations/transfer-org-ownership.js:177 +#: src/affiliation/mutations/transfer-org-ownership.js:146 +#: src/affiliation/mutations/transfer-org-ownership.js:169 +#: src/affiliation/mutations/transfer-org-ownership.js:180 msgid "Unable to transfer organization ownership. Please try again." msgstr "Impossible de transférer la propriété de l'organisation. Veuillez réessayer." @@ -1264,8 +1571,8 @@ msgstr "Impossible de transférer la propriété d'une organisation non définie msgid "Unable to transfer ownership to a user outside the org. Please invite the user and try again." msgstr "Impossible de transférer la propriété à un utilisateur extérieur à l'org. Veuillez inviter l'utilisateur et réessayer." -#: src/user/mutations/verify-phone-number.js:71 -#: src/user/mutations/verify-phone-number.js:78 +#: src/user/mutations/verify-phone-number.js:72 +#: src/user/mutations/verify-phone-number.js:80 msgid "Unable to two factor authenticate. Please try again." msgstr "Impossible de s'authentifier par deux facteurs. Veuillez réessayer." @@ -1273,8 +1580,8 @@ msgstr "Impossible de s'authentifier par deux facteurs. Veuillez réessayer." msgid "Unable to unfavourite domain, domain is not favourited." msgstr "Impossible de désactiver le domaine, le domaine n'est pas favorisé." -#: src/domain/mutations/unfavourite-domain.js:110 -#: src/domain/mutations/unfavourite-domain.js:117 +#: src/domain/mutations/unfavourite-domain.js:111 +#: src/domain/mutations/unfavourite-domain.js:119 msgid "Unable to unfavourite domain. Please try again." msgstr "Impossible de défavoriser le domaine. Veuillez réessayer." @@ -1282,32 +1589,36 @@ msgstr "Impossible de défavoriser le domaine. Veuillez réessayer." msgid "Unable to unfavourite unknown domain." msgstr "Impossible de défavoriser un domaine inconnu." -#: src/domain/mutations/update-domain.js:260 +#: src/domain/mutations/update-domain.js:284 msgid "Unable to update domain edge. Please try again." msgstr "Impossible de mettre à jour le bord du domaine. Veuillez réessayer." -#: src/domain/mutations/update-domain.js:149 +#: src/domain/mutations/update-domain.js:148 msgid "Unable to update domain in an unknown org." msgstr "Impossible de mettre à jour le domaine dans un org inconnu." -#: src/domain/mutations/update-domain.js:190 +#: src/domain/mutations/update-domain.js:189 msgid "Unable to update domain that does not belong to the given organization." msgstr "Impossible de mettre à jour un domaine qui n'appartient pas à l'organisation donnée." -#: src/domain/mutations/update-domain.js:180 -#: src/domain/mutations/update-domain.js:219 -#: src/domain/mutations/update-domain.js:301 -#: src/domain/mutations/update-domain.js:326 -#: src/domain/mutations/update-domain.js:348 -#: src/domain/mutations/update-domain.js:360 +#: src/domain/mutations/update-domain.js:179 +#: src/domain/mutations/update-domain.js:241 +#: src/domain/mutations/update-domain.js:295 msgid "Unable to update domain. Please try again." msgstr "Impossible de mettre à jour le domaine. Veuillez réessayer." -#: src/organization/mutations/update-organization.js:164 -#: src/organization/mutations/update-organization.js:190 -#: src/organization/mutations/update-organization.js:198 -#: src/organization/mutations/update-organization.js:243 -#: src/organization/mutations/update-organization.js:250 +#: src/domain/mutations/update-domains-by-domain-ids.js:68 +#: src/domain/mutations/update-domains-by-filters.js:76 +msgid "Unable to update domains in unknown organization." +msgstr "Impossible de mettre à jour les domaines dans une organisation inconnue." + +#: src/domain/mutations/update-domains-by-filters.js:229 +#: src/domain/mutations/update-domains-by-filters.js:237 +#: src/domain/mutations/update-domains-by-filters.js:244 +msgid "Unable to update domains. Please try again." +msgstr "Impossible de mettre à jour les domaines. Veuillez réessayer." + +#: src/organization/data-source.js:101 msgid "Unable to update organization. Please try again." msgstr "Impossible de mettre à jour l'organisation. Veuillez réessayer." @@ -1323,54 +1634,79 @@ msgstr "Impossible de mettre à jour le mot de passe, les nouveaux mots de passe msgid "Unable to update password, passwords do not match requirements. Please try again." msgstr "Impossible de mettre à jour le mot de passe, les mots de passe ne correspondent pas aux exigences. Veuillez réessayer." -#: src/user/mutations/update-user-password.js:94 -#: src/user/mutations/update-user-password.js:101 +#: src/user/mutations/update-user-password.js:95 +#: src/user/mutations/update-user-password.js:103 msgid "Unable to update password. Please try again." msgstr "Impossible de mettre à jour le mot de passe. Veuillez réessayer." -#: src/user/mutations/update-user-profile.js:160 -#: src/user/mutations/update-user-profile.js:167 +#: src/user/mutations/update-user-profile.js:154 +#: src/user/mutations/update-user-profile.js:162 msgid "Unable to update profile. Please try again." msgstr "Impossible de mettre à jour le profil. Veuillez réessayer." -#: src/affiliation/mutations/update-user-role.js:94 +#: src/affiliation/mutations/update-user-role.js:97 msgid "Unable to update role: organization unknown." msgstr "Impossible de mettre à jour le rôle : organisation inconnue." -#: src/affiliation/mutations/update-user-role.js:137 +#: src/affiliation/mutations/update-user-role.js:140 msgid "Unable to update role: user does not belong to organization." msgstr "Impossible de mettre à jour le rôle : l'utilisateur n'appartient pas à l'organisation." -#: src/affiliation/mutations/update-user-role.js:80 +#: src/affiliation/mutations/update-user-role.js:83 msgid "Unable to update role: user unknown." msgstr "Impossible de mettre à jour le rôle : utilisateur inconnu." -#: src/domain/mutations/update-domain.js:135 +#: src/tags/mutations/update-tag.js:113 +msgid "Unable to update tag in unknown organization." +msgstr "Impossible de mettre à jour la balise dans une organisation inconnue." + +#: src/tags/mutations/update-tag.js:103 +msgid "Unable to update tag, orgId is invalid." +msgstr "Impossible de mettre à jour la balise, l'orgId est invalide." + +#: src/tags/mutations/update-tag.js:127 +#~ msgid "Unable to update tag, tagId already in use." +#~ msgstr "Impossible de mettre à jour la balise, l'identifiant de balise est déjà utilisé." + +#: src/tags/data-source.js:30 +#: src/tags/data-source.js:36 +#: src/tags/data-source.js:92 +#: src/tags/data-source.js:101 +msgid "Unable to update tag. Please try again." +msgstr "Impossible de mettre à jour la balise. Veuillez réessayer." + +#: src/domain/mutations/update-domain.js:134 msgid "Unable to update unknown domain." msgstr "Impossible de mettre à jour un domaine inconnu." -#: src/organization/mutations/update-organization.js:130 +#: src/organization/mutations/update-organization.js:137 msgid "Unable to update unknown organization." msgstr "Impossible de mettre à jour une organisation inconnue." -#: src/affiliation/mutations/update-user-role.js:127 -#: src/affiliation/mutations/update-user-role.js:149 -#: src/affiliation/mutations/update-user-role.js:201 -#: src/affiliation/mutations/update-user-role.js:211 +#: src/tags/mutations/update-tag.js:87 +msgid "Unable to update unknown tag." +msgstr "Impossible de mettre à jour une étiquette inconnue." + +#: src/affiliation/mutations/update-user-role.js:130 +#: src/affiliation/mutations/update-user-role.js:152 +#: src/affiliation/mutations/update-user-role.js:196 +#: src/affiliation/mutations/update-user-role.js:206 +#: src/affiliation/mutations/update-user-role.js:217 msgid "Unable to update user's role. Please try again." msgstr "Impossible de mettre à jour le rôle de l'utilisateur. Veuillez réessayer." -#: src/affiliation/mutations/update-user-role.js:66 +#: src/affiliation/mutations/update-user-role.js:69 msgid "Unable to update your own role." msgstr "Impossible de mettre à jour votre propre rôle." -#: src/user/mutations/verify-account.js:49 +#: src/user/mutations/verify-account.js:51 #: src/user/mutations/verify-account.js:63 +#: src/user/mutations/verify-account.js:77 msgid "Unable to verify account. Please request a new email." msgstr "Impossible de vérifier le compte. Veuillez demander un nouvel e-mail." -#: src/user/mutations/verify-account.js:83 -#: src/user/mutations/verify-account.js:90 +#: src/user/mutations/verify-account.js:128 +#: src/user/mutations/verify-account.js:136 msgid "Unable to verify account. Please try again." msgstr "Impossible de vérifier le compte. Veuillez réessayer." @@ -1378,18 +1714,18 @@ msgstr "Impossible de vérifier le compte. Veuillez réessayer." msgid "Unable to verify if user is a super admin, please try again." msgstr "Impossible de vérifier si l'utilisateur est un super administrateur, veuillez réessayer." -#: src/user/mutations/update-user-profile.js:100 -#: src/user/queries/is-user-admin.js:55 +#: src/user/mutations/update-user-profile.js:99 +#: src/user/queries/is-user-admin.js:49 msgid "Unable to verify if user is an admin, please try again." msgstr "Impossible de vérifier si l'utilisateur est un administrateur, veuillez réessayer." -#: src/organization/mutations/verify-organization.js:105 -#: src/organization/mutations/verify-organization.js:121 -#: src/organization/mutations/verify-organization.js:128 +#: src/organization/data-source.js:237 +#: src/organization/data-source.js:252 +#: src/organization/data-source.js:260 msgid "Unable to verify organization. Please try again." msgstr "Impossible de vérifier l'organisation. Veuillez réessayer." -#: src/organization/mutations/verify-organization.js:53 +#: src/organization/mutations/verify-organization.js:50 msgid "Unable to verify unknown organization." msgstr "Impossible de vérifier une organisation inconnue." @@ -1397,43 +1733,52 @@ msgstr "Impossible de vérifier une organisation inconnue." msgid "User could not be queried." msgstr "L'utilisateur n'a pas pu être interrogé." -#: src/affiliation/mutations/update-user-role.js:244 +#: src/user/mutations/sign-up.js:79 +msgid "User is trying to register for a non-production environment." +msgstr "L'utilisateur essaie de s'enregistrer dans un environnement de non-production." + +#: src/affiliation/mutations/update-user-role.js:253 msgid "User role was updated successfully." msgstr "Le rôle de l'utilisateur a été mis à jour avec succès." -#: src/user/mutations/update-user-profile.js:81 +#: src/user/mutations/update-user-profile.js:80 +#: src/user/mutations/verify-account.js:88 msgid "Username not available, please try another." msgstr "Le nom d'utilisateur n'est pas disponible, veuillez en essayer un autre." -#: src/auth/tfa-required.js:15 +#: src/auth/guards/tfa-required.js:15 msgid "Verification error. Please activate multi-factor authentication to access content." msgstr "Erreur de vérification. Veuillez activer l'authentification multifactorielle pour accéder au contenu." -#: src/auth/verified-required.js:15 +#: src/auth/guards/verified-required.js:15 msgid "Verification error. Please verify your account via email to access content." msgstr "Erreur de vérification. Veuillez vérifier votre compte par e-mail pour accéder au contenu." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:70 +#: src/additional-findings/loaders/load-additional-findings-by-domain-id.js:8 +msgid "You must provide a `domainId` to retrieve a domain's additional findings." +msgstr "Vous devez fournir un `domainId` pour récupérer les résultats supplémentaires d'un domaine." + +#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:82 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:161 msgid "You must provide a `first` or `last` value to properly paginate the `Affiliation` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Affiliation`." -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:89 -#~ msgid "You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection." -#~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DKIMResults`." - #: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:114 #~ msgid "You must provide a `first` or `last` value to properly paginate the `DKIM` connection." #~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DKIM`." -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:138 -#~ msgid "You must provide a `first` or `last` value to properly paginate the `DMARC` connection." -#~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DMARC`." - #: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:36 msgid "You must provide a `first` or `last` value to properly paginate the `DkimFailureTable` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DkimFailureTable`." +#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:89 +#~ msgid "You must provide a `first` or `last` value to properly paginate the `DKIMResults` connection." +#~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DKIMResults`." + +#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:138 +#~ msgid "You must provide a `first` or `last` value to properly paginate the `DMARC` connection." +#~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DMARC`." + #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:36 msgid "You must provide a `first` or `last` value to properly paginate the `DmarcFailureTable` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DmarcFailureTable`." @@ -1443,7 +1788,7 @@ msgid "You must provide a `first` or `last` value to properly paginate the `Dmar msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DmarcSummaries`." #: src/domain/loaders/load-domain-connections-by-organizations-id.js:141 -#: src/domain/loaders/load-domain-connections-by-user-id.js:153 +#: src/domain/loaders/load-domain-connections-by-user-id.js:166 msgid "You must provide a `first` or `last` value to properly paginate the `Domain` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Domain`." @@ -1469,7 +1814,7 @@ msgid "You must provide a `first` or `last` value to properly paginate the `Log` msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Log`." #: src/organization/loaders/load-organization-connections-by-domain-id.js:166 -#: src/organization/loaders/load-organization-connections-by-user-id.js:166 +#: src/organization/loaders/load-organization-connections-by-user-id.js:178 msgid "You must provide a `first` or `last` value to properly paginate the `Organization` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Organization`." @@ -1477,15 +1822,15 @@ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctemen #~ msgid "You must provide a `first` or `last` value to properly paginate the `SPF` connection." #~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SPF`." -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:171 -#~ msgid "You must provide a `first` or `last` value to properly paginate the `SSL` connection." -#~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SSL`." - #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:36 msgid "You must provide a `first` or `last` value to properly paginate the `SpfFailureTable` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SpfFailureTable`." -#: src/user/loaders/load-user-connections-by-user-id.js:107 +#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:171 +#~ msgid "You must provide a `first` or `last` value to properly paginate the `SSL` connection." +#~ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `SSL`." + +#: src/user/loaders/load-user-connections-by-user-id.js:106 msgid "You must provide a `first` or `last` value to properly paginate the `User` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `User`." @@ -1504,8 +1849,8 @@ msgid "You must provide a `limit` value in the range of 1-100 to properly pagina msgstr "Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `DNS`." #: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:16 -msgid "You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection." -msgstr "Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `MXRecord`." +#~ msgid "You must provide a `limit` value in the range of 1-100 to properly paginate the `MXRecord` connection." +#~ msgstr "Vous devez fournir une valeur `limit` comprise entre 1 et 100 pour paginer correctement la connexion `MXRecord`." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:16 msgid "You must provide a `limit` value in the range of 1-100 to properly paginate the `web` connection." @@ -1516,157 +1861,53 @@ msgid "You must provide a `limit` value to properly paginate the `DNS` connectio msgstr "Vous devez fournir une valeur `limit` pour paginer correctement la connexion `DNS`." #: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:9 -msgid "You must provide a `limit` value to properly paginate the `MXRecord` connection." -msgstr "Vous devez fournir une valeur `limit` pour paginer correctement la connexion `MXRecord`." +#~ msgid "You must provide a `limit` value to properly paginate the `MXRecord` connection." +#~ msgstr "Vous devez fournir une valeur `limit` pour paginer correctement la connexion `MXRecord`." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:9 msgid "You must provide a `limit` value to properly paginate the `web` connection." msgstr "Vous devez fournir une valeur `limit` pour paginer correctement la connexion `web`." -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:9 -msgid "You must provide a `period` value to access the `ChartSummaries` connection." -msgstr "Vous devez fournir une valeur `period` pour accéder à la connexion `ChartSummaries`." +#: src/summaries/loaders/load-chart-summaries-by-period.js:8 +#~ msgid "You must provide a `period` value to access the `ChartSummaries` connection." +#~ msgstr "Vous devez fournir une valeur `period` pour accéder à la connexion `ChartSummaries`." #: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:12 msgid "You must provide a `period` value to access the `DmarcSummaries` connection." msgstr "Vous devez fournir une valeur `period` pour accéder à la connexion `DmarcSummaries`." #: src/organization/loaders/load-organization-summaries-by-period.js:10 -msgid "You must provide a `period` value to access the `OrganizationSummaries` connection." -msgstr "Vous devez fournir une valeur `period` pour accéder à la connexion `OrganizationSummaries`." +#~ msgid "You must provide a `period` value to access the `OrganizationSummaries` connection." +#~ msgstr "Vous devez fournir une valeur `period` pour accéder à la connexion `OrganizationSummaries`." -#: src/summaries/loaders/load-chart-summary-connections-by-period.js:30 -msgid "You must provide a `year` value to access the `ChartSummaries` connection." -msgstr "Vous devez fournir une valeur `year` pour accéder à la connexion `ChartSummaries`." +#: src/summaries/loaders/load-chart-summaries-by-period.js:43 +#~ msgid "You must provide a `year` value to access the `ChartSummaries` connection." +#~ msgstr "Vous devez fournir une valeur `year` pour accéder à la connexion `ChartSummaries`." #: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:18 msgid "You must provide a `year` value to access the `DmarcSummaries` connection." msgstr "Vous devez fournir une valeur `year` pour accéder à la connexion `DmarcSummaries`." -#: src/organization/loaders/load-organization-summaries-by-period.js:32 -msgid "You must provide a `year` value to access the `OrganizationSummaries` connection." -msgstr "Vous devez fournir une valeur `year` pour accéder à la connexion `OrganizationSummaries`." +#: src/organization/loaders/load-organization-summaries-by-period.js:46 +#~ msgid "You must provide a `year` value to access the `OrganizationSummaries` connection." +#~ msgstr "Vous devez fournir une valeur `year` pour accéder à la connexion `OrganizationSummaries`." #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:30 msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `DNS` connection." msgstr "Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `DNS`." #: src/dns-scan/loaders/load-mx-record-diff-by-domain-id.js:30 -msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection." -msgstr "Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `MXRecord`." +#~ msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `MXRecord` connection." +#~ msgstr "Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `MXRecord`." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:30 msgid "You must provide at most one pagination method (`before`, `after`, `offset`) value to properly paginate the `web` connection." msgstr "Vous devez fournir au plus une valeur de méthode de pagination (`before`, `after`, `offset`) pour paginer correctement la connexion `web`." -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:109 -#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:208 -#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:131 -#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:83 -#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:83 -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:202 -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:83 -#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:83 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:178 -#: src/domain/loaders/load-domain-connections-by-user-id.js:192 -#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:132 -#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:136 -#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:136 -#: src/organization/loaders/load-organization-connections-by-domain-id.js:203 -#: src/organization/loaders/load-organization-connections-by-user-id.js:201 -#: src/user/loaders/load-user-connections-by-user-id.js:154 -#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:164 -#: src/verified-domains/loaders/load-verified-domain-connections.js:164 -#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:214 -#: src/verified-organizations/loaders/load-verified-organizations-connections.js:212 -msgid "`{argSet}` must be of type `number` not `{typeSet}`." -msgstr "`{argSet}` doit être de type `number` et non `{typeSet}`." - -#: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:86 -#: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:182 -msgid "`{argSet}` on the `Affiliation` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `Affiliation` ne peut être inférieur à zéro." - -#: src/email-scan/loaders/load-dkim-results-connections-by-dkim-id.js:110 -#~ msgid "`{argSet}` on the `DKIMResults` connection cannot be less than zero." -#~ msgstr "`{argSet}` sur la connexion `DKIMResults` ne peut être inférieur à zéro." - -#: src/email-scan/loaders/load-dkim-connections-by-domain-id.js:135 -#~ msgid "`{argSet}` on the `DKIM` connection cannot be less than zero." -#~ msgstr "`{argSet}` sur la connexion `DKIM` ne peut être inférieur à zéro." +#: src/summaries/loaders/load-chart-summaries-by-period.js:13 +#~ msgid "You must provide both `startDate` and `endDate` values to access the `ChartSummaries` connection." +#~ msgstr "Vous devez fournir les valeurs « startDate » et « endDate » pour accéder à la connexion « ChartSummaries »." -#: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:160 -#~ msgid "`{argSet}` on the `DMARC` connection cannot be less than zero." -#~ msgstr "`{argSet}` sur la connexion `DMARC` ne peut être inférieur à zéro." - -#: src/dmarc-summaries/loaders/load-dkim-failure-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `DkimFailureTable` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `DkimFailureTable` ne peut être inférieur à zéro." - -#: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `DmarcFailureTable` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `DmarcFailureTable` ne peut être inférieur à zéro." - -#: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:179 -msgid "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro." - -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:157 -#: src/domain/loaders/load-domain-connections-by-user-id.js:169 -msgid "`{argSet}` on the `Domain` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `Domain` ne peut être inférieur à zéro." - -#: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `FullPassTable` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `FullPassTable` ne peut être inférieur à zéro." - -#: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:106 -#: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-https-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-spf-guidance-tags-connections.js:110 -#: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:110 -msgid "`{argSet}` on the `GuidanceTag` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `GuidanceTag` ne peut être inférieure à zéro." - -#: src/web-scan/loaders/load-https-connections-by-domain-id.js:168 -#~ msgid "`{argSet}` on the `HTTPS` connection cannot be less than zero." -#~ msgstr "`{argSet}` sur la connexion `HTTPS` ne peut être inférieur à zéro." - -#: src/audit-logs/loaders/load-audit-logs-by-org-id.js:112 -msgid "`{argSet}` on the `Log` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `Log` ne peut être inférieur à zéro." - -#: src/organization/loaders/load-organization-connections-by-domain-id.js:182 -#: src/organization/loaders/load-organization-connections-by-user-id.js:180 -msgid "`{argSet}` on the `Organization` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `Organization` ne peut être inférieure à zéro." - -#: src/email-scan/loaders/load-spf-connections-by-domain-id.js:154 -#~ msgid "`{argSet}` on the `SPF` connection cannot be less than zero." -#~ msgstr "`{argSet}` sur la connexion `SPF` ne peut être inférieure à zéro." - -#: src/web-scan/loaders/load-ssl-connections-by-domain-id.js:192 -#~ msgid "`{argSet}` on the `SSL` connection cannot be less than zero." -#~ msgstr "`{argSet}` sur la connexion `SSL` ne peut être inférieur à zéro." - -#: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:57 -msgid "`{argSet}` on the `SpfFailureTable` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `SpfFailureTable` ne peut être inférieur à zéro." - -#: src/user/loaders/load-user-connections-by-user-id.js:128 -msgid "`{argSet}` on the `User` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `User` ne peut être inférieure à zéro." - -#: src/verified-domains/loaders/load-verified-domain-connections-by-organization-id.js:138 -#: src/verified-domains/loaders/load-verified-domain-connections.js:138 -msgid "`{argSet}` on the `VerifiedDomain` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro." - -#: src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js:188 -#: src/verified-organizations/loaders/load-verified-organizations-connections.js:186 -msgid "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." -msgstr "`{argSet}` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro." +#: src/organization/loaders/load-organization-summaries-by-period.js:13 +#~ msgid "You must provide both `startDate` and `endDate` values to access the `OrganizationSummaries` connection." +#~ msgstr "Vous devez fournir les valeurs « startDate » et « endDate » pour accéder à la connexion « OrganizationSummaries »." diff --git a/api/src/mutation.js b/api/src/mutation.js index 44b8af16a..2bdcb1ddf 100644 --- a/api/src/mutation.js +++ b/api/src/mutation.js @@ -1,9 +1,10 @@ -import {GraphQLObjectType} from 'graphql' +import { GraphQLObjectType } from 'graphql' import * as affiliationMutations from './affiliation/mutations' import * as domainMutations from './domain/mutations' import * as organizationMutations from './organization/mutations' import * as userMutations from './user/mutations' +import * as tagMutations from './tags/mutations' export const createMutationSchema = () => { return new GraphQLObjectType({ @@ -17,6 +18,7 @@ export const createMutationSchema = () => { ...organizationMutations, // User Mutations ...userMutations, + ...tagMutations, }), }) } diff --git a/api/src/notify/__tests__/notify-send-org-invite-create-account.test.js b/api/src/notify/__tests__/notify-send-org-invite-create-account.test.js index f76dea6b5..feccc6e33 100644 --- a/api/src/notify/__tests__/notify-send-org-invite-create-account.test.js +++ b/api/src/notify/__tests__/notify-send-org-invite-create-account.test.js @@ -55,7 +55,6 @@ describe('given the sendOrgInviteCreateAccount function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'english', } const mockedSendOrgInviteCreateAccount = sendOrgInviteCreateAccount({ @@ -93,7 +92,6 @@ describe('given the sendOrgInviteCreateAccount function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'english', } try { @@ -141,7 +139,6 @@ describe('given the sendOrgInviteCreateAccount function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'french', } const mockedSendOrgInviteCreateAccount = sendOrgInviteCreateAccount({ @@ -179,7 +176,6 @@ describe('given the sendOrgInviteCreateAccount function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'french', } try { diff --git a/api/src/notify/__tests__/notify-send-org-invite-email.test.js b/api/src/notify/__tests__/notify-send-org-invite-email.test.js index cbc798a78..59dabcc56 100644 --- a/api/src/notify/__tests__/notify-send-org-invite-email.test.js +++ b/api/src/notify/__tests__/notify-send-org-invite-email.test.js @@ -55,7 +55,6 @@ describe('given the sendOrgInviteEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'english', } const mockedSendOrgInviteEmail = sendOrgInviteEmail({ @@ -87,7 +86,6 @@ describe('given the sendOrgInviteEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'english', } try { @@ -135,7 +133,6 @@ describe('given the sendOrgInviteEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'french', } const mockedSendOrgInviteEmail = sendOrgInviteEmail({ @@ -167,7 +164,6 @@ describe('given the sendOrgInviteEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'french', } try { diff --git a/api/src/notify/__tests__/notify-send-password-reset-email.test.js b/api/src/notify/__tests__/notify-send-password-reset-email.test.js index 93551dd17..b906d15d6 100644 --- a/api/src/notify/__tests__/notify-send-password-reset-email.test.js +++ b/api/src/notify/__tests__/notify-send-password-reset-email.test.js @@ -56,7 +56,6 @@ describe('given the sendPasswordResetEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'english', } const mockedSendPasswordResetEmail = sendPasswordResetEmail({ @@ -86,7 +85,6 @@ describe('given the sendPasswordResetEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'english', } try { @@ -133,7 +131,6 @@ describe('given the sendPasswordResetEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'french', } const mockedSendPasswordResetEmail = sendPasswordResetEmail({ @@ -163,7 +160,6 @@ describe('given the sendPasswordResetEmail function', () => { const user = { userName: 'test@email.ca', displayName: 'Test Account', - preferredLang: 'french', } try { diff --git a/api/src/notify/__tests__/notify-send-tfa-text-msg.test.js b/api/src/notify/__tests__/notify-send-tfa-text-msg.test.js deleted file mode 100644 index ef9301cd3..000000000 --- a/api/src/notify/__tests__/notify-send-tfa-text-msg.test.js +++ /dev/null @@ -1,192 +0,0 @@ -import {setupI18n} from '@lingui/core' - -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import {sendTfaTextMsg} from '../index' - -const { - NOTIFICATION_TWO_FACTOR_CODE_EN, - NOTIFICATION_TWO_FACTOR_CODE_FR, -} = process.env - -describe('given the sendTfaTextMsg function', () => { - let i18n - let consoleOutput = [] - const mockedError = (output) => consoleOutput.push(output) - - beforeAll(async () => { - console.error = mockedError - i18n = setupI18n({ - locale: 'en', - localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - - beforeEach(async () => { - consoleOutput = [] - }) - describe('language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('text message successfully sent', () => { - it('returns nothing', async () => { - const sendSms = jest.fn() - const notifyClient = { - sendSms, - } - const user = { - phoneNumber: '+12345678901', - tfaCode: 123456, - preferredLang: 'english', - } - - const mockedSendTfaTextMsg = sendTfaTextMsg({notifyClient, i18n}) - await mockedSendTfaTextMsg({ - phoneNumber: user.phoneNumber, - user, - }) - - expect(notifyClient.sendSms).toHaveBeenCalledWith( - NOTIFICATION_TWO_FACTOR_CODE_EN, - user.phoneNumber, - { - personalisation: { - verify_code: user.tfaCode, - }, - }, - ) - }) - }) - describe('an error occurs while sending text message', () => { - it('throws an error message', async () => { - const sendSms = jest - .fn() - .mockRejectedValue(new Error('Notification error occurred.')) - const notifyClient = { - sendSms, - } - const user = { - phoneNumber: '+12345678901', - tfaCode: 123456, - preferredLang: 'english', - } - - try { - const mockedSendTfaTextMsg = sendTfaTextMsg({notifyClient, i18n}) - await mockedSendTfaTextMsg({ - phoneNumber: user.phoneNumber, - user, - }) - } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to send two factor authentication message. Please try again.', - ), - ) - } - - expect(consoleOutput).toEqual([ - `Error occurred when sending two factor authentication message for ${user._key}: Error: Notification error occurred.`, - ]) - }) - }) - }) - describe('language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('text message successfully sent', () => { - it('returns nothing', async () => { - const sendSms = jest.fn() - const notifyClient = { - sendSms, - } - const user = { - phoneNumber: '+12345678901', - tfaCode: 123456, - preferredLang: 'french', - } - - const mockedSendTfaTextMsg = sendTfaTextMsg({notifyClient, i18n}) - await mockedSendTfaTextMsg({ - phoneNumber: user.phoneNumber, - user, - }) - - expect(notifyClient.sendSms).toHaveBeenCalledWith( - NOTIFICATION_TWO_FACTOR_CODE_FR, - user.phoneNumber, - { - personalisation: { - verify_code: user.tfaCode, - }, - }, - ) - }) - }) - describe('an error occurs while sending text message', () => { - it('throws an error message', async () => { - const sendSms = jest - .fn() - .mockRejectedValue(new Error('Notification error occurred.')) - const notifyClient = { - sendSms, - } - const user = { - phoneNumber: '+12345678901', - tfaCode: 123456, - preferredLang: 'french', - } - - try { - const mockedSendTfaTextMsg = sendTfaTextMsg({notifyClient, i18n}) - await mockedSendTfaTextMsg({ - phoneNumber: user.phoneNumber, - user, - }) - } catch (err) { - expect(err).toEqual( - new Error( - "Impossible d'envoyer le message d'authentification à deux facteurs. Veuillez réessayer.", - ), - ) - } - - expect(consoleOutput).toEqual([ - `Error occurred when sending two factor authentication message for ${user._key}: Error: Notification error occurred.`, - ]) - }) - }) - }) -}) diff --git a/api/src/notify/__tests__/notify-send-verification-email.test.js b/api/src/notify/__tests__/notify-send-verification-email.test.js deleted file mode 100644 index 93f5b663d..000000000 --- a/api/src/notify/__tests__/notify-send-verification-email.test.js +++ /dev/null @@ -1,204 +0,0 @@ -import {setupI18n} from '@lingui/core' - -import englishMessages from '../../locale/en/messages' -import frenchMessages from '../../locale/fr/messages' -import {sendVerificationEmail} from '../index' - -const { - NOTIFICATION_VERIFICATION_EMAIL_EN, - NOTIFICATION_VERIFICATION_EMAIL_FR, -} = process.env - -describe('given the sendVerificationEmail function', () => { - let i18n - let consoleOutput = [] - const mockedError = (output) => consoleOutput.push(output) - - beforeAll(async () => { - console.error = mockedError - i18n = setupI18n({ - locale: 'en', - localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - - beforeEach(async () => { - consoleOutput = [] - }) - describe('language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('email successfully sent', () => { - it('returns nothing', async () => { - const sendEmail = jest.fn() - const notifyClient = { - sendEmail, - } - const user = { - userName: 'test.email@email.ca', - displayName: 'Test Account', - preferredLang: 'english', - } - - const mockedSendVerificationEmail = sendVerificationEmail({ - notifyClient, - i18n, - }) - await mockedSendVerificationEmail({ - verifyUrl: 'verify.url', - user, - }) - - expect(notifyClient.sendEmail).toHaveBeenCalledWith( - NOTIFICATION_VERIFICATION_EMAIL_EN, - user.userName, - { - personalisation: { - user: user.displayName, - verify_email_url: 'verify.url', - }, - }, - ) - }) - }) - describe('an error occurs while sending email', () => { - it('throws an error message', async () => { - const sendEmail = jest - .fn() - .mockRejectedValue(new Error('Notification error occurred.')) - const notifyClient = { - sendEmail, - } - const user = { - userName: 'test.email@email.ca', - displayName: 'Test Account', - preferredLang: 'english', - } - - try { - const mockedSendVerificationEmail = sendVerificationEmail({ - notifyClient, - i18n, - }) - await mockedSendVerificationEmail({ - verifyUrl: 'verify.url', - user, - }) - } catch (err) { - expect(err).toEqual( - new Error('Unable to send verification email. Please try again.'), - ) - } - - expect(consoleOutput).toEqual([ - `Error occurred when sending verification email for ${user._key}: Error: Notification error occurred.`, - ]) - }) - }) - }) - describe('language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('email successfully sent', () => { - it('returns nothing', async () => { - const sendEmail = jest.fn() - const notifyClient = { - sendEmail, - } - const user = { - userName: 'test.email@email.ca', - displayName: 'Test Account', - preferredLang: 'french', - } - - const mockedSendVerificationEmail = sendVerificationEmail({ - notifyClient, - i18n, - }) - await mockedSendVerificationEmail({ - verifyUrl: 'verify.url', - user, - }) - - expect(notifyClient.sendEmail).toHaveBeenCalledWith( - NOTIFICATION_VERIFICATION_EMAIL_FR, - user.userName, - { - personalisation: { - user: user.displayName, - verify_email_url: 'verify.url', - }, - }, - ) - }) - }) - describe('an error occurs while sending email', () => { - it('throws an error message', async () => { - const sendEmail = jest - .fn() - .mockRejectedValue(new Error('Notification error occurred.')) - const notifyClient = { - sendEmail, - } - const user = { - userName: 'test.email@email.ca', - displayName: 'Test Account', - preferredLang: 'french', - } - - try { - const mockedSendVerificationEmail = sendVerificationEmail({ - notifyClient, - i18n, - }) - await mockedSendVerificationEmail({ - verifyUrl: 'verify.url', - user, - }) - } catch (err) { - expect(err).toEqual( - new Error( - "Impossible d'envoyer l'email de vérification. Veuillez réessayer.", - ), - ) - } - - expect(consoleOutput).toEqual([ - `Error occurred when sending verification email for ${user._key}: Error: Notification error occurred.`, - ]) - }) - }) - }) -}) diff --git a/api/src/notify/index.js b/api/src/notify/index.js index 0a1fc75a1..9e306a9a0 100644 --- a/api/src/notify/index.js +++ b/api/src/notify/index.js @@ -5,5 +5,6 @@ export * from './notify-send-invite-request-email' export * from './notify-send-org-invite-create-account' export * from './notify-send-org-invite-email' export * from './notify-send-password-reset-email' -export * from './notify-send-tfa-text-msg' +export * from './notify-send-updated-username-email' export * from './notify-send-verification-email' +export * from './notify-send-role-change-email' diff --git a/api/src/notify/notify-send-role-change-email.js b/api/src/notify/notify-send-role-change-email.js new file mode 100644 index 000000000..473339b69 --- /dev/null +++ b/api/src/notify/notify-send-role-change-email.js @@ -0,0 +1,20 @@ +const { NOTIFICATION_ROLE_CHANGE_EMAIL } = process.env + +export const sendRoleChangeEmail = + ({ notifyClient }) => + async ({ user, orgNames, oldRole, newRole }) => { + try { + await notifyClient.sendEmail(NOTIFICATION_ROLE_CHANGE_EMAIL, user.userName, { + personalisation: { + display_name: user.displayName, + org_name_en: orgNames.orgNameEN, + org_name_fr: orgNames.orgNameFR, + old_role: oldRole, + new_role: newRole, + }, + }) + return true + } catch (err) { + console.error(`Error occurred when sending role update email for ${user._key}: ${err}`) + } + } diff --git a/api/src/notify/notify-send-tfa-text-msg.js b/api/src/notify/notify-send-tfa-text-msg.js deleted file mode 100644 index 0548ed408..000000000 --- a/api/src/notify/notify-send-tfa-text-msg.js +++ /dev/null @@ -1,34 +0,0 @@ -import {t} from '@lingui/macro' - -const { - NOTIFICATION_TWO_FACTOR_CODE_EN, - NOTIFICATION_TWO_FACTOR_CODE_FR, -} = process.env - -export const sendTfaTextMsg = ({notifyClient, i18n}) => async ({ - phoneNumber, - user, - }) => { - let templateId = NOTIFICATION_TWO_FACTOR_CODE_EN - if (user.preferredLang === 'french') { - templateId = NOTIFICATION_TWO_FACTOR_CODE_FR - } - - try { - await notifyClient.sendSms(templateId, phoneNumber, { - personalisation: { - verify_code: user.tfaCode, - }, - }) - return true - } catch (err) { - console.error( - `Error occurred when sending two factor authentication message for ${user._key}: ${err}`, - ) - throw new Error( - i18n._( - t`Unable to send two factor authentication message. Please try again.`, - ), - ) - } -} diff --git a/api/src/notify/notify-send-updated-username-email.js b/api/src/notify/notify-send-updated-username-email.js new file mode 100644 index 000000000..d5c819a00 --- /dev/null +++ b/api/src/notify/notify-send-updated-username-email.js @@ -0,0 +1,20 @@ +import { t } from '@lingui/macro' + +const { NOTIFICATION_USERNAME_UPDATED_BILINGUAL } = process.env + +export const sendUpdatedUserNameEmail = + ({ notifyClient, i18n }) => + async ({ previousUserName, newUserName, displayName, userKey }) => { + try { + await notifyClient.sendEmail(NOTIFICATION_USERNAME_UPDATED_BILINGUAL, previousUserName, { + personalisation: { + user: displayName, + new_username: newUserName, + }, + }) + return true + } catch (err) { + console.error(`Error occurred when sending updated username email for ${userKey}: ${err}`) + throw new Error(i18n._(t`Unable to send updated username email. Please try again.`)) + } + } diff --git a/api/src/notify/notify-send-verification-email.js b/api/src/notify/notify-send-verification-email.js index b1e8dcdd7..33ec8c43d 100644 --- a/api/src/notify/notify-send-verification-email.js +++ b/api/src/notify/notify-send-verification-email.js @@ -1,33 +1,20 @@ -import {t} from '@lingui/macro' +import { t } from '@lingui/macro' -const { - NOTIFICATION_VERIFICATION_EMAIL_EN, - NOTIFICATION_VERIFICATION_EMAIL_FR, -} = process.env +const { NOTIFICATION_VERIFICATION_EMAIL_BILINGUAL } = process.env -export const sendVerificationEmail = ({notifyClient, i18n}) => async ({ - user, - verifyUrl, - }) => { - let templateId = NOTIFICATION_VERIFICATION_EMAIL_EN - if (user.preferredLang === 'french') { - templateId = NOTIFICATION_VERIFICATION_EMAIL_FR +export const sendVerificationEmail = + ({ notifyClient, i18n }) => + async ({ userName, displayName, verifyUrl, userKey }) => { + try { + await notifyClient.sendEmail(NOTIFICATION_VERIFICATION_EMAIL_BILINGUAL, userName, { + personalisation: { + user: displayName, + verify_email_url: verifyUrl, + }, + }) + return true + } catch (err) { + console.error(`Error occurred when sending verification email for ${userKey}: ${err}`) + throw new Error(i18n._(t`Unable to send verification email. Please try again.`)) + } } - - try { - await notifyClient.sendEmail(templateId, user.userName, { - personalisation: { - user: user.displayName, - verify_email_url: verifyUrl, - }, - }) - return true - } catch (err) { - console.error( - `Error occurred when sending verification email for ${user._key}: ${err}`, - ) - throw new Error( - i18n._(t`Unable to send verification email. Please try again.`), - ) - } -} diff --git a/api/src/organization/data-source.js b/api/src/organization/data-source.js new file mode 100644 index 000000000..e8698c258 --- /dev/null +++ b/api/src/organization/data-source.js @@ -0,0 +1,502 @@ +import { t } from '@lingui/macro' + +import { + loadAllOrganizationDomainStatuses, + loadOrgByKey, + loadOrgBySlug, + loadOrgConnectionsByDomainId, + loadOrgConnectionsByUserId, + loadOrganizationDomainStatuses, + loadOrganizationNamesById, + loadOrganizationSummariesByPeriod, +} from './loaders' + +export class OrganizationDataSource { + constructor({ query, userKey, i18n, language, cleanseInput, loginRequiredBool, transaction, collections }) { + this._query = query + this._userKey = userKey + this._i18n = i18n + this._transaction = transaction + this._collections = collections + this.byKey = loadOrgByKey({ query, language, userKey, i18n }) + this.bySlug = loadOrgBySlug({ query, language, userKey, i18n }) + this.connectionsByDomainId = loadOrgConnectionsByDomainId({ query, language, userKey, cleanseInput, i18n, auth: { loginRequiredBool } }) + this.connectionsByUserId = loadOrgConnectionsByUserId({ query, userKey, cleanseInput, language, i18n, auth: { loginRequiredBool } }) + this.summariesByPeriod = loadOrganizationSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + this.namesById = loadOrganizationNamesById({ query, userKey, i18n }) + this.domainStatuses = loadOrganizationDomainStatuses({ query, userKey, i18n }) + this.allDomainStatuses = loadAllOrganizationDomainStatuses({ query, userKey, i18n, language }) + } + + async create({ organizationDetails, userId, language }) { + const trx = await this._transaction(this._collections) + + let cursor + try { + cursor = await trx.step( + () => this._query` + WITH organizations + INSERT ${organizationDetails} INTO organizations + RETURN MERGE( + { + _id: NEW._id, + _key: NEW._key, + _rev: NEW._rev, + _type: "organization", + id: NEW._key, + verified: NEW.verified, + domainCount: 0, + policies: NEW.policies, + summaries: null + }, + TRANSLATE(${language}, NEW.orgDetails) + ) + `, + ) + } catch (err) { + console.error(`Database error occurred when user: ${this._userKey} was creating new organization: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create organization. Please try again.`)) + } + const organization = await cursor.next() + + try { + await trx.step( + () => this._query` + WITH affiliations, organizations, users + INSERT { + _from: ${organization._id}, + _to: ${userId}, + permission: "owner", + } INTO affiliations + `, + ) + } catch (err) { + console.error(`Database error occurred when inserting affiliation for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction error occurred when committing new organization for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create organization. Please try again.`)) + } + + return organization + } + + async checkNameInUse({ nameEN, nameFR }) { + let cursor + try { + cursor = await this._query` + WITH organizations + FOR org IN organizations + FILTER (org.orgDetails.en.name == ${nameEN}) OR (org.orgDetails.fr.name == ${nameFR}) + RETURN org + ` + } catch (err) { + console.error(`Database error occurred during name check for user: ${this._userKey}: ${err}`) + throw new Error(this._i18n._(t`Unable to update organization. Please try again.`)) + } + return cursor + } + + async getRawByKey({ orgKey }) { + let cursor + try { + cursor = await this._query` + WITH organizations + FOR org IN organizations + FILTER org._key == ${orgKey} + RETURN org + ` + } catch (err) { + console.error(`Database error occurred while retrieving org: ${orgKey} for user: ${this._userKey}: ${err}`) + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + + try { + return await cursor.next() + } catch (err) { + console.error(`Cursor error occurred while retrieving org: ${orgKey} for user: ${this._userKey}: ${err}`) + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + } + + async update({ orgKey, updatedOrgDetails }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH organizations + UPSERT { _key: ${orgKey} } + INSERT ${updatedOrgDetails} + UPDATE ${updatedOrgDetails} + IN organizations + `, + ) + } catch (err) { + console.error(`Transaction error occurred while upserting org: ${orgKey} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction error occurred while committing org: ${orgKey} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + } + + async archive({ organization }) { + const trx = await this._transaction(this._collections) + + let domainInfo + try { + const countCursor = await this._query` + WITH claims, domains, organizations + LET domainIds = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} claims + RETURN e._to + ) + FOR domain IN domains + FILTER domain._id IN domainIds + LET count = LENGTH( + FOR v, e IN 1..1 INBOUND domain._id claims + RETURN 1 + ) + RETURN { _id: domain._id, _key: domain._key, count } + ` + domainInfo = await countCursor.all() + } catch (err) { + console.error(`Database error occurred for user: ${this._userKey} while gathering domain count for archive of org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + + for (const domain of domainInfo) { + if (domain.count === 1) { + try { + await trx.step( + () => this._query` + WITH domains + UPDATE { _key: ${domain._key}, archived: true } IN domains + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while archiving domains for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + } + } + + try { + await trx.step( + () => this._query` + WITH organizations + UPDATE { _key: ${organization._key}, verified: false } IN organizations + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while unverifying org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred for user: ${this._userKey} while archiving org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + } + + async verify({ currentOrg }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH organizations + UPSERT { _key: ${currentOrg._key} } + INSERT ${currentOrg} + UPDATE ${currentOrg} + IN organizations + `, + ) + } catch (err) { + console.error(`Transaction error occurred while upserting verified org: ${currentOrg._key} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to verify organization. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH domains, claims + FOR v, e IN 1..1 OUTBOUND ${currentOrg._id} claims + FILTER v.archived == true + UPDATE v WITH { archived: false } IN domains + `, + ) + } catch (err) { + console.error(`Transaction error occurred while unarchiving affiliated domains for org: ${currentOrg._key} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to verify organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction error occurred while committing verified org: ${currentOrg._key} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to verify organization. Please try again.`)) + } + } + + async remove({ organization }) { + const trx = await this._transaction(this._collections) + + let dmarcSummaryCheckList + try { + const dmarcSummaryCheckCursor = await this._query` + WITH domains, ownership, dmarcSummaries, organizations + FOR v, e IN 1..1 OUTBOUND ${organization._id} ownership + RETURN e + ` + dmarcSummaryCheckList = await dmarcSummaryCheckCursor.all() + } catch (err) { + console.error(`Database error occurred for user: ${this._userKey} while getting dmarc summaries for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + for (const ownership of dmarcSummaryCheckList) { + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries + LET dmarcSummaryEdges = ( + FOR v, e IN 1..1 OUTBOUND ${ownership._to} domainsToDmarcSummaries + RETURN { edgeKey: e._key, dmarcSummaryId: e._to } + ) + LET removeDmarcSummaryEdges = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries + OPTIONS { waitForSync: true } + ) + LET removeDmarcSummary = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key + REMOVE key IN dmarcSummaries + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing dmarc summaries for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains + REMOVE ${ownership._key} IN ownership + OPTIONS { waitForSync: true } + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing ownerships for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + } + + let domainInfo + try { + const countCursor = await this._query` + WITH claims, domains, organizations + LET domainIds = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} claims + RETURN e._to + ) + FOR domain IN domains + FILTER domain._id IN domainIds + LET count = LENGTH( + FOR v, e IN 1..1 INBOUND domain._id claims + RETURN 1 + ) + RETURN { + "_id": domain._id, + "_key": domain._key, + "domain": domain.domain, + "count": count + } + ` + domainInfo = await countCursor.all() + } catch (err) { + console.error(`Database error occurred for user: ${this._userKey} while gathering domain count for removal of org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + for (const domain of domainInfo) { + if (domain.count === 1) { + try { + await trx.step(async () => { + await this._query` + WITH web, webScan + FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb + LET removeWebScansQuery = ( + FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans + REMOVE webScanV IN webScan + REMOVE webToWebScansV IN webToWebScans + OPTIONS { waitForSync: true } + ) + REMOVE webV IN web + REMOVE domainsWebEdge IN domainsWeb + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing web data for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH dns + FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS + REMOVE dnsV IN dns + REMOVE domainsDNSEdge IN domainsDNS + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing DNS data for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH favourites, domains + FOR fav IN favourites + FILTER fav._to == ${domain._id} + REMOVE fav IN favourites + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing favourites for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + FOR e IN domainsToSelectors + FILTER e._from == ${domain._id} + REMOVE e IN domainsToSelectors + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing DKIM selectors for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH claims, domains, organizations + LET domainEdges = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} claims + FILTER e._to == ${domain._id} + RETURN { edgeKey: e._key, domainId: e._to } + ) + LET removeDomainEdges = ( + FOR domainEdge in domainEdges + REMOVE domainEdge.edgeKey IN claims + OPTIONS { waitForSync: true } + ) + LET removeDomain = ( + FOR domainEdge in domainEdges + LET key = PARSE_IDENTIFIER(domainEdge.domainId).key + REMOVE key IN domains + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing domains for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + } + } + + try { + await trx.step( + () => this._query` + WITH affiliations, organizations, users + LET userEdges = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} affiliations + RETURN { edgeKey: e._key, userKey: e._to } + ) + LET removeUserEdges = ( + FOR userEdge IN userEdges + REMOVE userEdge.edgeKey IN affiliations + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + + await trx.step( + () => this._query` + WITH organizations, organizationSummaries + FOR summary in organizationSummaries + FILTER summary.organization == ${organization._id} + REMOVE summary._key IN organizationSummaries + OPTIONS { waitForSync: true } + `, + ) + + await trx.step( + () => this._query` + WITH organizations + REMOVE ${organization._key} IN organizations + OPTIONS { waitForSync: true } + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing affiliations and org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred for user: ${this._userKey} while removing org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + } +} diff --git a/api/src/organization/index.js b/api/src/organization/index.js index e170187ec..9366445c5 100644 --- a/api/src/organization/index.js +++ b/api/src/organization/index.js @@ -1,3 +1,4 @@ +export * from './data-source' export * from './inputs' export * from './loaders' export * from './mutations' diff --git a/api/src/organization/loaders/__tests__/load-organization-by-key.test.js b/api/src/organization/loaders/__tests__/load-organization-by-key.test.js index 6003e9f1a..476961a06 100644 --- a/api/src/organization/loaders/__tests__/load-organization-by-key.test.js +++ b/api/src/organization/loaders/__tests__/load-organization-by-key.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,7 +10,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadOrgByKey dataloader', () => { - let query, drop, truncate, collections, i18n + let query, drop, truncate, collections, db, i18n const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -22,32 +23,25 @@ describe('given a loadOrgByKey dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) beforeEach(async () => { - await collections.organizations.save({ - verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, + const org1 = await collections.organizations.save({ + verified: false, + externalId: 'test', + policies: { + psd: false, + pgs: false, }, orgDetails: { en: { @@ -72,19 +66,19 @@ describe('given a loadOrgByKey dataloader', () => { }, }, }) - await collections.organizations.save({ - verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, + const summary1 = await collections.organizationSummaries.save({ + organization: org1._id, + web: { pass: 50, fail: 1000, total: 1050 }, + mail: { pass: 50, fail: 1000, total: 1050 }, + }) + await db.collection('organizations').update(org1._key, { latestSummaryId: summary1._id }) + + const org2 = await collections.organizations.save({ + verified: false, + externalId: 'test', + policies: { + psd: false, + pgs: false, }, orgDetails: { en: { @@ -109,6 +103,12 @@ describe('given a loadOrgByKey dataloader', () => { }, }, }) + const summary2 = await collections.organizationSummaries.save({ + organization: org2._id, + web: { pass: 50, fail: 1000, total: 1050 }, + mail: { pass: 50, fail: 1000, total: 1050 }, + }) + await db.collection('organizations').update(org2._key, { latestSummaryId: summary2._id }) }) afterEach(async () => { await truncate() @@ -138,7 +138,7 @@ describe('given a loadOrgByKey dataloader', () => { FOR org IN organizations FILTER org.orgDetails.en.slug == "communications-security-establishment" LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("en", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, externalId: org.externalId, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("en", org.orgDetails)) ` const expectedOrg = await expectedCursor.next() @@ -155,7 +155,7 @@ describe('given a loadOrgByKey dataloader', () => { const expectedCursor = await query` FOR org IN organizations LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("en", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, externalId: org.externalId, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("en", org.orgDetails)) ` while (expectedCursor.hasMore) { @@ -192,7 +192,7 @@ describe('given a loadOrgByKey dataloader', () => { FOR org IN organizations FILTER org.orgDetails.fr.slug == "centre-de-la-securite-des-telecommunications" LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("fr", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, externalId: org.externalId, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("fr", org.orgDetails)) ` const expectedOrg = await expectedCursor.next() @@ -209,7 +209,7 @@ describe('given a loadOrgByKey dataloader', () => { const expectedCursor = await query` FOR org IN organizations LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("fr", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, _rev: org._rev, _type: "organization", id: org._key, verified: org.verified, externalId: org.externalId, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("fr", org.orgDetails)) ` while (expectedCursor.hasMore) { @@ -243,9 +243,7 @@ describe('given a loadOrgByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadOrgByKey({ query: mockedQuery, language: 'en', @@ -256,9 +254,7 @@ describe('given a loadOrgByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load organization(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -284,9 +280,7 @@ describe('given a loadOrgByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load organization(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -312,9 +306,7 @@ describe('given a loadOrgByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadOrgByKey({ query: mockedQuery, language: 'fr', @@ -325,11 +317,7 @@ describe('given a loadOrgByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -355,11 +343,7 @@ describe('given a loadOrgByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/organization/loaders/__tests__/load-organization-by-slug.test.js b/api/src/organization/loaders/__tests__/load-organization-by-slug.test.js index b34b2c9da..13fbe95b2 100644 --- a/api/src/organization/loaders/__tests__/load-organization-by-slug.test.js +++ b/api/src/organization/loaders/__tests__/load-organization-by-slug.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,7 +10,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadOrgBySlug dataloader', () => { - let query, drop, truncate, collections, i18n + let query, drop, truncate, collections, db, i18n const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -22,32 +23,25 @@ describe('given a loadOrgBySlug dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) beforeEach(async () => { - await collections.organizations.save({ - verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, + const org1 = await collections.organizations.save({ + verified: false, + externalId: 'test', + policies: { + psd: false, + pgs: false, }, orgDetails: { en: { @@ -72,8 +66,27 @@ describe('given a loadOrgBySlug dataloader', () => { }, }, }) + const summary1 = await collections.organizationSummaries.save({ + organization: org1._id, + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, + }) + await db.collection('organizations').update(org1._key, { latestSummaryId: summary1._id }) await collections.organizations.save({ - verified: true, + verified: false, + externalId: 'test', + policies: { + psd: false, + pgs: false, + }, orgDetails: { en: { slug: 'treasury-board-secretariat', @@ -134,8 +147,10 @@ describe('given a loadOrgBySlug dataloader', () => { _type: "organization", id: org._key, verified: org.verified, + externalId: org.externalId, + policies: org.policies, domainCount: COUNT(domains), - summaries: org.summaries, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null, slugEN: org.orgDetails.en.slug, slugFR: org.orgDetails.fr.slug }, @@ -165,8 +180,9 @@ describe('given a loadOrgBySlug dataloader', () => { _type: "organization", id: org._key, verified: org.verified, + policies: org.policies, domainCount: COUNT(domains), - summaries: org.summaries, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null, slugEN: org.orgDetails.en.slug, slugFR: org.orgDetails.fr.slug }, @@ -216,11 +232,13 @@ describe('given a loadOrgBySlug dataloader', () => { _type: "organization", id: org._key, verified: org.verified, + externalId: org.externalId, + policies: org.policies, domainCount: COUNT(domains), - summaries: org.summaries, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null, slugEN: org.orgDetails.en.slug, slugFR: org.orgDetails.fr.slug - }, + }, TRANSLATE("fr", org.orgDetails) ) ` @@ -247,11 +265,12 @@ describe('given a loadOrgBySlug dataloader', () => { _type: "organization", id: org._key, verified: org.verified, + policies: org.policies, domainCount: COUNT(domains), - summaries: org.summaries, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null, slugEN: org.orgDetails.en.slug, slugFR: org.orgDetails.fr.slug - }, + }, TRANSLATE("fr", org.orgDetails) ) ` @@ -287,9 +306,7 @@ describe('given a loadOrgBySlug dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadOrgBySlug({ query: mockedQuery, language: 'en', @@ -300,9 +317,7 @@ describe('given a loadOrgBySlug dataloader', () => { try { await loader.load('slug') } catch (err) { - expect(err).toEqual( - new Error('Unable to load organization(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -328,9 +343,7 @@ describe('given a loadOrgBySlug dataloader', () => { try { await loader.load('slug') } catch (err) { - expect(err).toEqual( - new Error('Unable to load organization(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -356,9 +369,7 @@ describe('given a loadOrgBySlug dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadOrgBySlug({ query: mockedQuery, language: 'fr', @@ -369,11 +380,7 @@ describe('given a loadOrgBySlug dataloader', () => { try { await loader.load('slug') } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -399,11 +406,7 @@ describe('given a loadOrgBySlug dataloader', () => { try { await loader.load('slug') } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/organization/loaders/__tests__/load-organization-connections-by-domain-id.test.js b/api/src/organization/loaders/__tests__/load-organization-connections-by-domain-id.test.js index 7a930dfca..1e7ef4c31 100644 --- a/api/src/organization/loaders/__tests__/load-organization-connections-by-domain-id.test.js +++ b/api/src/organization/loaders/__tests__/load-organization-connections-by-domain-id.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -12,17 +13,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the load organizations connection function', () => { - let query, - drop, - truncate, - collections, - user, - org, - orgTwo, - domain, - domainTwo, - domainThree, - i18n + let query, drop, truncate, collections, db, user, org, orgTwo, domain, domainTwo, domainThree, i18n const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -37,40 +28,28 @@ describe('given the load organizations connection function', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) org = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-one', @@ -94,20 +73,23 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgSummary = await collections.organizationSummaries.save({ + organization: org._id, + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, + }) + await db.collection("organizations").update(org._key, { latestSummaryId: orgSummary._id }) orgTwo = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-two', @@ -131,6 +113,20 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + web: { + pass: 52, + fail: 1002, + total: 1054, + }, + mail: { + pass: 52, + fail: 1002, + total: 1054, + }, + }) + await db.collection("organizations").update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) await collections.affiliations.save({ _from: org._id, _to: user._id, @@ -201,10 +197,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -251,10 +244,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -301,10 +291,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -350,10 +337,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -496,10 +480,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) const connectionArgs = { domainId: domain._id, @@ -543,18 +524,7 @@ describe('given the load organizations connection function', () => { beforeEach(async () => { orgThree = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-three', @@ -578,6 +548,20 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + web: { + pass: 51, + fail: 1001, + total: 1052, + }, + mail: { + pass: 51, + fail: 1001, + total: 1052, + }, + }) + await db.collection("organizations").update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) await collections.affiliations.save({ _from: orgThree._id, _to: user._id, @@ -2206,10 +2190,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2262,10 +2243,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) const connectionArgs = { first: 5, @@ -2302,18 +2280,7 @@ describe('given the load organizations connection function', () => { beforeEach(async () => { saOrg = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'super-admin', @@ -2337,6 +2304,20 @@ describe('given the load organizations connection function', () => { }, }, }) + const saOrgSummary = await collections.organizationSummaries.save({ + organization: saOrg._id, + web: { + pass: 52, + fail: 1002, + total: 1054, + }, + mail: { + pass: 52, + fail: 1002, + total: 1054, + }, + }) + await db.collection("organizations").update(saOrg._key, { latestSummaryId: saOrgSummary._id }) await collections.affiliations.save({ _from: saOrg._id, _to: user._id, @@ -2359,11 +2340,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -2419,11 +2396,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -2492,10 +2465,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2542,10 +2512,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2592,10 +2559,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2641,10 +2605,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2787,10 +2748,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) const connectionArgs = { domainId: domain._id, @@ -2834,18 +2792,7 @@ describe('given the load organizations connection function', () => { beforeEach(async () => { orgThree = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-three', @@ -2869,6 +2816,20 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + web: { + pass: 51, + fail: 1001, + total: 1052, + }, + mail: { + pass: 51, + fail: 1001, + total: 1052, + }, + }) + await db.collection("organizations").update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) await collections.affiliations.save({ _from: orgThree._id, _to: user._id, @@ -4497,10 +4458,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -4553,10 +4511,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key]) const connectionArgs = { first: 5, @@ -4593,18 +4548,7 @@ describe('given the load organizations connection function', () => { beforeEach(async () => { saOrg = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'super-admin', @@ -4628,6 +4572,20 @@ describe('given the load organizations connection function', () => { }, }, }) + const saOrgSummary = await collections.organizationSummaries.save({ + organization: saOrg._id, + web: { + pass: 52, + fail: 1002, + total: 1054, + }, + mail: { + pass: 52, + fail: 1002, + total: 1054, + }, + }) + await db.collection("organizations").update(saOrg._key, { latestSummaryId: saOrgSummary._id }) await collections.affiliations.save({ _from: saOrg._id, _to: user._id, @@ -4650,11 +4608,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -4710,11 +4664,7 @@ describe('given the load organizations connection function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - org._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([org._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -4857,11 +4807,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `Organization` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `Organization` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -4889,11 +4835,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `Organization` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `Organization` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -4971,9 +4913,7 @@ describe('given the load organizations connection function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByDomainId({ query, language: 'en', @@ -4992,11 +4932,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -5008,9 +4944,7 @@ describe('given the load organizations connection function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByDomainId({ query, language: 'en', @@ -5029,11 +4963,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: ${ @@ -5048,9 +4978,7 @@ describe('given the load organizations connection function', () => { describe('given a database error', () => { describe('when gathering organizations', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadOrgConnectionsByDomainId({ query, @@ -5070,9 +4998,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load organization(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -5108,11 +5034,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to load organization(s). Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -5224,9 +5146,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `Organization` ne peut être inférieure à zéro.', - ), + new Error('`first` sur la connexion `Organization` ne peut être inférieure à zéro.'), ) } @@ -5255,11 +5175,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` sur la connexion `Organization` ne peut être inférieure à zéro.', - ), - ) + expect(err).toEqual(new Error('`last` sur la connexion `Organization` ne peut être inférieure à zéro.')) } expect(consoleOutput).toEqual([ @@ -5337,9 +5253,7 @@ describe('given the load organizations connection function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByDomainId({ query, language: 'fr', @@ -5359,9 +5273,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -5374,9 +5286,7 @@ describe('given the load organizations connection function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByDomainId({ query, language: 'fr', @@ -5396,9 +5306,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -5414,9 +5322,7 @@ describe('given the load organizations connection function', () => { describe('given a database error', () => { describe('when gathering organizations', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadOrgConnectionsByDomainId({ query, @@ -5436,11 +5342,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -5476,11 +5378,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/organization/loaders/__tests__/load-organization-connections-by-user-id.test.js b/api/src/organization/loaders/__tests__/load-organization-connections-by-user-id.test.js index d8c2b6a41..8d2380c3e 100644 --- a/api/src/organization/loaders/__tests__/load-organization-connections-by-user-id.test.js +++ b/api/src/organization/loaders/__tests__/load-organization-connections-by-user-id.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -12,17 +13,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the load organization connections by user id function', () => { - let query, - drop, - truncate, - collections, - user, - orgOne, - orgTwo, - i18n, - domain, - domainTwo, - domainThree + let query, drop, truncate, collections, db, user, orgOne, orgTwo, i18n, domain, domainTwo, domainThree const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -38,40 +29,28 @@ describe('given the load organization connections by user id function', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) orgOne = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-one', @@ -95,20 +74,23 @@ describe('given the load organization connections by user id function', () => { }, }, }) + const orgOneSummary = await collections.organizationSummaries.save({ + organization: orgOne._id, + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, + }) + await db.collection("organizations").update(orgOne._key, { latestSummaryId: orgOneSummary._id }) orgTwo = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-two', @@ -132,6 +114,20 @@ describe('given the load organization connections by user id function', () => { }, }, }) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + web: { + pass: 52, + fail: 1002, + total: 1054, + }, + mail: { + pass: 52, + fail: 1002, + total: 1054, + }, + }) + await db.collection("organizations").update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) await collections.affiliations.save({ _from: orgOne._id, _to: user._id, @@ -203,10 +199,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -250,10 +243,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -297,10 +287,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -343,10 +330,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -481,10 +465,7 @@ describe('given the load organization connections by user id function', () => { describe('search field is left empty', () => { it('returns unfiltered organizations', async () => { const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) const connectionLoader = loadOrgConnectionsByUserId({ query, @@ -522,10 +503,7 @@ describe('given the load organization connections by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'organization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('organization', expectedOrgs[0]._key), endCursor: toGlobalId('organization', expectedOrgs[1]._key), }, } @@ -539,18 +517,7 @@ describe('given the load organization connections by user id function', () => { beforeEach(async () => { orgThree = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-three', @@ -574,6 +541,20 @@ describe('given the load organization connections by user id function', () => { }, }, }) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + web: { + pass: 51, + fail: 1001, + total: 1052, + }, + mail: { + pass: 51, + fail: 1001, + total: 1052, + }, + }) + await db.collection("organizations").update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) await collections.affiliations.save({ _from: orgThree._id, _to: user._id, @@ -2125,6 +2106,56 @@ describe('given the load organization connections by user id function', () => { }) }) }) + describe('isAffiliated is set to true', () => { + it('returns organizations', async () => { + const connectionLoader = loadOrgConnectionsByUserId({ + query, + userKey: user._key, + cleanseInput, + auth: { loginRequired: true }, + language: 'en', + i18n, + }) + + const connectionArgs = { + first: 5, + isAffiliated: true, + } + const orgs = await connectionLoader({ ...connectionArgs }) + + const orgLoader = loadOrgByKey({ query, language: 'en' }) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) + + expectedOrgs[0].id = expectedOrgs[0]._key + expectedOrgs[1].id = expectedOrgs[1]._key + + const expectedStructure = { + edges: [ + { + cursor: toGlobalId('organization', expectedOrgs[0]._key), + node: { + ...expectedOrgs[0], + }, + }, + { + cursor: toGlobalId('organization', expectedOrgs[1]._key), + node: { + ...expectedOrgs[1], + }, + }, + ], + totalCount: 2, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: toGlobalId('organization', expectedOrgs[0]._key), + endCursor: toGlobalId('organization', expectedOrgs[1]._key), + }, + } + + expect(orgs).toEqual(expectedStructure) + }) + }) describe('isSuperAdmin is set to true', () => { it('returns organizations', async () => { const connectionLoader = loadOrgConnectionsByUserId({ @@ -2143,10 +2174,7 @@ describe('given the load organization connections by user id function', () => { const orgs = await connectionLoader({ ...connectionArgs }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2190,10 +2218,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) const connectionArgs = { first: 5, @@ -2227,18 +2252,7 @@ describe('given the load organization connections by user id function', () => { beforeEach(async () => { saOrg = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'super-admin', @@ -2262,6 +2276,20 @@ describe('given the load organization connections by user id function', () => { }, }, }) + const saOrgSummary = await collections.organizationSummaries.save({ + organization: saOrg._id, + web: { + pass: 52, + fail: 1002, + total: 1054, + }, + mail: { + pass: 52, + fail: 1002, + total: 1054, + }, + }) + await db.collection("organizations").update(saOrg._key, { latestSummaryId: saOrgSummary._id }) await collections.affiliations.save({ _from: saOrg._id, _to: user._id, @@ -2280,11 +2308,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -2317,10 +2341,7 @@ describe('given the load organization connections by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'organization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('organization', expectedOrgs[0]._key), endCursor: toGlobalId('organization', expectedOrgs[2]._key), }, } @@ -2340,11 +2361,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'en' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -2371,10 +2388,7 @@ describe('given the load organization connections by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'organization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('organization', expectedOrgs[0]._key), endCursor: toGlobalId('organization', expectedOrgs[1]._key), }, } @@ -2447,10 +2461,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2494,10 +2505,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2541,10 +2549,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2587,10 +2592,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -2626,18 +2628,7 @@ describe('given the load organization connections by user id function', () => { beforeEach(async () => { orgThree = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'slug-org-three', @@ -2661,6 +2652,20 @@ describe('given the load organization connections by user id function', () => { }, }, }) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + web: { + pass: 51, + fail: 1001, + total: 1052, + }, + mail: { + pass: 51, + fail: 1001, + total: 1052, + }, + }) + await db.collection("organizations").update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) await collections.affiliations.save({ _from: orgThree._id, _to: user._id, @@ -4316,10 +4321,7 @@ describe('given the load organization connections by user id function', () => { describe('search field is left empty', () => { it('returns unfiltered organizations', async () => { const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) const connectionLoader = loadOrgConnectionsByUserId({ query, @@ -4357,10 +4359,7 @@ describe('given the load organization connections by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'organization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('organization', expectedOrgs[0]._key), endCursor: toGlobalId('organization', expectedOrgs[1]._key), }, } @@ -4387,10 +4386,7 @@ describe('given the load organization connections by user id function', () => { const orgs = await connectionLoader({ ...connectionArgs }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) expectedOrgs[0].id = expectedOrgs[0]._key expectedOrgs[1].id = expectedOrgs[1]._key @@ -4434,10 +4430,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key]) const connectionArgs = { first: 5, @@ -4471,18 +4464,7 @@ describe('given the load organization connections by user id function', () => { beforeEach(async () => { saOrg = await collections.organizations.save({ verified: false, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, + externalId: 'test', orgDetails: { en: { slug: 'super-admin', @@ -4506,6 +4488,20 @@ describe('given the load organization connections by user id function', () => { }, }, }) + const saOrgSummary = await collections.organizationSummaries.save({ + organization: saOrg._id, + web: { + pass: 52, + fail: 1002, + total: 1054, + }, + mail: { + pass: 52, + fail: 1002, + total: 1054, + }, + }) + await db.collection("organizations").update(saOrg._key, { latestSummaryId: saOrgSummary._id }) await collections.affiliations.save({ _from: saOrg._id, _to: user._id, @@ -4524,11 +4520,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -4561,10 +4553,7 @@ describe('given the load organization connections by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'organization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('organization', expectedOrgs[0]._key), endCursor: toGlobalId('organization', expectedOrgs[2]._key), }, } @@ -4584,11 +4573,7 @@ describe('given the load organization connections by user id function', () => { }) const orgLoader = loadOrgByKey({ query, language: 'fr' }) - const expectedOrgs = await orgLoader.loadMany([ - orgOne._key, - orgTwo._key, - saOrg._key, - ]) + const expectedOrgs = await orgLoader.loadMany([orgOne._key, orgTwo._key, saOrg._key]) const connectionArgs = { first: 5, @@ -4615,10 +4600,7 @@ describe('given the load organization connections by user id function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: false, - startCursor: toGlobalId( - 'organization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('organization', expectedOrgs[0]._key), endCursor: toGlobalId('organization', expectedOrgs[1]._key), }, } @@ -4761,11 +4743,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `Organization` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `Organization` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -4792,11 +4770,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `Organization` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `Organization` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -4872,9 +4846,7 @@ describe('given the load organization connections by user id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByUserId({ query, userKey: 123, @@ -4893,11 +4865,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: 123 attempted to have \`first\` set as a ${typeof invalidInput} for: loadOrgConnectionsByUserId.`, @@ -4907,9 +4875,7 @@ describe('given the load organization connections by user id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByUserId({ query, userKey: 123, @@ -4928,11 +4894,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User: 123 attempted to have \`last\` set as a ${typeof invalidInput} for: loadOrgConnectionsByUserId.`, @@ -4945,11 +4907,7 @@ describe('given the load organization connections by user id function', () => { describe('given a database error', () => { describe('while querying domains', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue( - new Error('Unable to query organizations. Please try again.'), - ) + const query = jest.fn().mockRejectedValue(new Error('Unable to query organizations. Please try again.')) const connectionLoader = loadOrgConnectionsByUserId({ query, @@ -4968,9 +4926,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load organization(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -4984,9 +4940,7 @@ describe('given the load organization connections by user id function', () => { it('returns an error message', async () => { const cursor = { next() { - throw new Error( - 'Unable to load organizations. Please try again.', - ) + throw new Error('Unable to load organizations. Please try again.') }, } const query = jest.fn().mockReturnValueOnce(cursor) @@ -5008,9 +4962,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load organization(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -5118,9 +5070,7 @@ describe('given the load organization connections by user id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `Organization` ne peut être inférieure à zéro.', - ), + new Error('`first` sur la connexion `Organization` ne peut être inférieure à zéro.'), ) } @@ -5148,11 +5098,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` sur la connexion `Organization` ne peut être inférieure à zéro.', - ), - ) + expect(err).toEqual(new Error('`last` sur la connexion `Organization` ne peut être inférieure à zéro.')) } expect(consoleOutput).toEqual([ @@ -5228,9 +5174,7 @@ describe('given the load organization connections by user id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByUserId({ query, userKey: 123, @@ -5250,9 +5194,7 @@ describe('given the load organization connections by user id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -5263,9 +5205,7 @@ describe('given the load organization connections by user id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadOrgConnectionsByUserId({ query, userKey: 123, @@ -5285,9 +5225,7 @@ describe('given the load organization connections by user id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -5301,11 +5239,7 @@ describe('given the load organization connections by user id function', () => { describe('given a database error', () => { describe('while querying domains', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue( - new Error('Unable to query organizations. Please try again.'), - ) + const query = jest.fn().mockRejectedValue(new Error('Unable to query organizations. Please try again.')) const connectionLoader = loadOrgConnectionsByUserId({ query, @@ -5324,11 +5258,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ @@ -5342,9 +5272,7 @@ describe('given the load organization connections by user id function', () => { it('returns an error message', async () => { const cursor = { next() { - throw new Error( - 'Unable to load organizations. Please try again.', - ) + throw new Error('Unable to load organizations. Please try again.') }, } const query = jest.fn().mockReturnValueOnce(cursor) @@ -5366,11 +5294,7 @@ describe('given the load organization connections by user id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - "Impossible de charger l'organisation (s). Veuillez réessayer.", - ), - ) + expect(err).toEqual(new Error("Impossible de charger l'organisation (s). Veuillez réessayer.")) } expect(consoleOutput).toEqual([ diff --git a/api/src/organization/loaders/__tests__/load-organization-summaries-by-period.test.js b/api/src/organization/loaders/__tests__/load-organization-summaries-by-period.test.js new file mode 100644 index 000000000..5ec899d0a --- /dev/null +++ b/api/src/organization/loaders/__tests__/load-organization-summaries-by-period.test.js @@ -0,0 +1,49 @@ +import { loadOrganizationSummariesByPeriod } from '../load-organization-summaries-by-period' +import { createI18n } from '../../../create-i18n' + +describe('loadOrganizationSummariesByPeriod', () => { + let query, userKey, cleanseInput + + const i18n = createI18n('en') + + beforeEach(() => { + query = jest.fn() + userKey = '1' + cleanseInput = jest.fn((input) => input) + }) + + it('returns summaries for a given startDate and endDate', async () => { + const loader = loadOrganizationSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + const mockSummaries = [ + { _key: '1', date: '2023-01-01' }, + { _key: '2', date: '2023-01-02' }, + ] + query.mockResolvedValueOnce({ + all: jest.fn().mockResolvedValueOnce(mockSummaries), + }) + + const result = await loader({ orgId: 'org1', startDate: '2023-01-01', endDate: '2023-01-31', sortDirection: 'ASC' }) + + expect(result).toEqual(mockSummaries) + }) + + it('handles database errors gracefully', async () => { + const loader = loadOrganizationSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + query.mockRejectedValueOnce(new Error('Database error')) + + await expect( + loader({ orgId: 'org1', startDate: '2023-01-01', endDate: '2023-01-31', sortDirection: 'ASC' }), + ).rejects.toThrow('Unable to load organization summary data. Please try again.') + }) + + it('handles cursor errors gracefully', async () => { + const loader = loadOrganizationSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + query.mockResolvedValueOnce({ + all: jest.fn().mockRejectedValueOnce(new Error('Cursor error')), + }) + + await expect( + loader({ orgId: 'org1', startDate: '2023-01-01', endDate: '2023-01-31', sortDirection: 'ASC' }), + ).rejects.toThrow('Unable to load organization summary data. Please try again.') + }) +}) diff --git a/api/src/organization/loaders/index.js b/api/src/organization/loaders/index.js index 1e5991965..82b0be23f 100644 --- a/api/src/organization/loaders/index.js +++ b/api/src/organization/loaders/index.js @@ -5,3 +5,4 @@ export * from './load-organization-connections-by-user-id' export * from './load-all-organization-domain-statuses' export * from './load-organization-domain-statuses' export * from './load-organization-summaries-by-period' +export * from './load-organization-names-by-id' diff --git a/api/src/organization/loaders/load-all-organization-domain-statuses.js b/api/src/organization/loaders/load-all-organization-domain-statuses.js index 20ea5b8fb..5910cb48d 100644 --- a/api/src/organization/loaders/load-all-organization-domain-statuses.js +++ b/api/src/organization/loaders/load-all-organization-domain-statuses.js @@ -1,61 +1,168 @@ import { t } from '@lingui/macro' +import { aql } from 'arangojs' export const loadAllOrganizationDomainStatuses = - ({ query, userKey, i18n }) => - async ({ blocked }) => { - let statuses + ({ query, userKey, i18n, language }) => + async ({ filters }) => { + let domains + let domainFilters = aql`` + let archivedFilter = aql`FILTER d.archived != true` + if (typeof filters !== 'undefined') { + filters.forEach(({ filterCategory, comparison, filterValue }) => { + if (comparison === '==') { + comparison = aql`==` + } else { + comparison = aql`!=` + } + if (filterCategory === 'dmarc-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.dmarc ${comparison} ${filterValue} + ` + } else if (filterCategory === 'dkim-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.dkim ${comparison} ${filterValue} + ` + } else if (filterCategory === 'https-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.https ${comparison} ${filterValue} + ` + } else if (filterCategory === 'spf-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.spf ${comparison} ${filterValue} + ` + } else if (filterCategory === 'ciphers-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.ciphers ${comparison} ${filterValue} + ` + } else if (filterCategory === 'curves-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.curves ${comparison} ${filterValue} + ` + } else if (filterCategory === 'hsts-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.hsts ${comparison} ${filterValue} + ` + } else if (filterCategory === 'protocols-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.protocols ${comparison} ${filterValue} + ` + } else if (filterCategory === 'certificates-status') { + domainFilters = aql` + ${domainFilters} + FILTER d.status.certificates ${comparison} ${filterValue} + ` + } else if (filterCategory === 'tags') { + if (filterValue === 'nxdomain') { + domainFilters = aql` + ${domainFilters} + FILTER d.rcode ${comparison} "NXDOMAIN" + ` + } else if (filterValue === 'blocked') { + domainFilters = aql` + ${domainFilters} + FILTER d.blocked ${comparison} true + ` + } else if (filterValue === 'wildcard-sibling') { + domainFilters = aql` + ${domainFilters} + FILTER d.wildcardSibling ${comparison} true + ` + } else if (filterValue === 'wildcard-entry') { + domainFilters = aql` + ${domainFilters} + FILTER d.wildcardEntry ${comparison} true + ` + } else if (filterValue === 'scan-pending') { + domainFilters = aql`${domainFilters}` + } else if (filterValue === 'has-entrust-certificate') { + domainFilters = aql` + ${domainFilters} + FILTER d.hasEntrustCertificate ${comparison} true + ` + } else if (filterValue === 'cve-detected') { + domainFilters = aql` + ${domainFilters} + FILTER d.cveDetected ${comparison} true + ` + } else if (filterValue === 'archived') { + archivedFilter = aql`FILTER d.archived ${comparison} true` + } + } else if (filterCategory === 'dmarc-phase') { + domainFilters = aql` + ${domainFilters} + FILTER v.phase ${comparison} ${filterValue} + ` + } + }) + } try { - if (blocked) { - statuses = ( - await query` - WITH domains, organizations - FOR org IN organizations - FILTER org.orgDetails.en.acronym != "SA" - FOR domain, claim IN 1..1 OUTBOUND org._id claims - FILTER domain.blocked == true - RETURN { - "Organization name (English)": org.orgDetails.en.name, - "Nom de l'organisation (Français)": org.orgDetails.fr.name, - "Domain": domain.domain, - "HTTPS": domain.status.https, - "HSTS": domain.status.hsts, - "Ciphers": domain.status.ciphers, - "Curves": domain.status.curves, - "Protocols": domain.status.protocols, - "SPF": domain.status.spf, - "DKIM": domain.status.dkim, - "DMARC": domain.status.dmarc - } - ` - ).all() - } else { - statuses = ( - await query` - WITH domains, organizations - FOR org IN organizations - FILTER org.orgDetails.en.acronym != "SA" - FOR domain, claim IN 1..1 OUTBOUND org._id claims - RETURN { - "Organization name (English)": org.orgDetails.en.name, - "Nom de l'organisation (Français)": org.orgDetails.fr.name, - "Domain": domain.domain, - "HTTPS": domain.status.https, - "HSTS": domain.status.hsts, - "Ciphers": domain.status.ciphers, - "Curves": domain.status.curves, - "Protocols": domain.status.protocols, - "SPF": domain.status.spf, - "DKIM": domain.status.dkim, - "DMARC": domain.status.dmarc - } - ` - ).all() - } + domains = ( + await query` + WITH domains + FOR d IN domains + ${archivedFilter} + ${domainFilters} + LET ipAddresses = FIRST( + FILTER d.latestDnsScan + LET latestDnsScan = DOCUMENT(d.latestDnsScan) + FILTER latestDnsScan.resolveIps + RETURN latestDnsScan.resolveIps + ) + LET vulnerabilities = ( + FOR finding IN additionalFindings + FILTER finding.domain == d._id + LIMIT 1 + RETURN UNIQUE( + FOR wc IN finding.webComponents + FILTER LENGTH(wc.WebComponentCves) > 0 + FOR vuln IN wc.WebComponentCves + FILTER vuln.Cve NOT IN (d.ignoredCves || []) + RETURN vuln.Cve + ) + )[0] + LET verifiedOrgs = ( + FOR v,e IN 1..1 INBOUND d._id claims + FILTER v.verified == true + RETURN MERGE({ externalId: v.externalId || null }, TRANSLATE(${language}, v.orgDetails)) + ) + RETURN { + "domain": d.domain, + "orgNames": verifiedOrgs[*].name, + "orgAcronyms": verifiedOrgs[*].acronym, + "orgExternalIDs": verifiedOrgs[*].externalId, + "ipAddresses": ipAddresses, + "https": d.status.https, + "hsts": d.status.hsts, + "certificates": d.status.certificates, + "ciphers": d.status.ciphers, + "curves": d.status.curves, + "protocols": d.status.protocols, + "spf": d.status.spf, + "dkim": d.status.dkim, + "dmarc": d.status.dmarc, + "phase": d.phase, + "rcode": d.rcode, + "blocked": d.blocked, + "wildcardSibling": d.wildcardSibling, + "wildcardEntry": d.wildcardEntry, + "hasEntrustCertificate": d.hasEntrustCertificate, + "top25Vulnerabilities": vulnerabilities + } + ` + ).all() } catch (err) { - console.error(`Database error occurred when user: ${userKey} running loadAllOrganizationDomainStatuses: ${err}`) - throw new Error(i18n._(t`Unable to load all organization domain statuses. Please try again.`)) + console.error(`Database error occurred when user: ${userKey} running loadOrganizationDomainStatuses: ${err}`) + throw new Error(i18n._(t`Unable to load organization domain statuses. Please try again.`)) } - return statuses + return domains } diff --git a/api/src/organization/loaders/load-organization-by-key.js b/api/src/organization/loaders/load-organization-by-key.js index 8ab995c43..9d566dfbe 100644 --- a/api/src/organization/loaders/load-organization-by-key.js +++ b/api/src/organization/loaders/load-organization-by-key.js @@ -19,19 +19,17 @@ export const loadOrgByKey = ({ query, language, userKey, i18n }) => _type: "organization", id: org._key, verified: org.verified, + externalId: org.externalId, domainCount: COUNT(orgDomains), - summaries: org.summaries - }, + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null + }, TRANSLATE(${language}, org.orgDetails) ) ` } catch (err) { - console.error( - `Database error occurred when user: ${userKey} running loadOrgByKey: ${err}`, - ) - throw new Error( - i18n._(t`Unable to load organization(s). Please try again.`), - ) + console.error(`Database error occurred when user: ${userKey} running loadOrgByKey: ${err}`) + throw new Error(i18n._(t`Unable to load organization(s). Please try again.`)) } const orgMap = {} @@ -40,12 +38,8 @@ export const loadOrgByKey = ({ query, language, userKey, i18n }) => orgMap[org._key] = org }) } catch (err) { - console.error( - `Cursor error occurred when user: ${userKey} during loadOrgByKey: ${err}`, - ) - throw new Error( - i18n._(t`Unable to load organization(s). Please try again.`), - ) + console.error(`Cursor error occurred when user: ${userKey} during loadOrgByKey: ${err}`) + throw new Error(i18n._(t`Unable to load organization(s). Please try again.`)) } return ids.map((id) => orgMap[id]) diff --git a/api/src/organization/loaders/load-organization-by-slug.js b/api/src/organization/loaders/load-organization-by-slug.js index 74daddc9a..6a5934902 100644 --- a/api/src/organization/loaders/load-organization-by-slug.js +++ b/api/src/organization/loaders/load-organization-by-slug.js @@ -20,8 +20,10 @@ export const loadOrgBySlug = ({ query, language, userKey, i18n }) => _type: "organization", id: org._key, verified: org.verified, + externalId: org.externalId, domainCount: COUNT(orgDomains), - summaries: org.summaries, + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null, slugEN: org.orgDetails.en.slug, slugFR: org.orgDetails.fr.slug }, @@ -29,12 +31,8 @@ export const loadOrgBySlug = ({ query, language, userKey, i18n }) => ) ` } catch (err) { - console.error( - `Database error occurred when user: ${userKey} running loadOrgBySlug: ${err}`, - ) - throw new Error( - i18n._(t`Unable to load organization(s). Please try again.`), - ) + console.error(`Database error occurred when user: ${userKey} running loadOrgBySlug: ${err}`) + throw new Error(i18n._(t`Unable to load organization(s). Please try again.`)) } const orgMap = {} @@ -44,12 +42,8 @@ export const loadOrgBySlug = ({ query, language, userKey, i18n }) => orgMap[org.slugFR] = org }) } catch (err) { - console.error( - `Cursor error occurred when user: ${userKey} running loadOrgBySlug: ${err}`, - ) - throw new Error( - i18n._(t`Unable to load organization(s). Please try again.`), - ) + console.error(`Cursor error occurred when user: ${userKey} running loadOrgBySlug: ${err}`) + throw new Error(i18n._(t`Unable to load organization(s). Please try again.`)) } return slugs.map((slug) => orgMap[slug]) diff --git a/api/src/organization/loaders/load-organization-connections-by-domain-id.js b/api/src/organization/loaders/load-organization-connections-by-domain-id.js index f7e572944..ea2527815 100644 --- a/api/src/organization/loaders/load-organization-connections-by-domain-id.js +++ b/api/src/organization/loaders/load-organization-connections-by-domain-id.js @@ -52,23 +52,23 @@ export const loadOrgConnectionsByDomainId = documentField = aql`afterVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`afterVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`afterVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`afterVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`afterVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`afterVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`afterVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND afterVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -127,23 +127,23 @@ export const loadOrgConnectionsByDomainId = documentField = aql`beforeVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`beforeVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`beforeVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`beforeVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`beforeVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`beforeVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`beforeVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND beforeVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -254,27 +254,27 @@ export const loadOrgConnectionsByDomainId = hasNextPageDocumentField = aql`LAST(retrievedOrgs).verified` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).verified` } else if (orderBy.field === 'summary-mail-pass') { - orgField = aql`org.summaries.mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - orgField = aql`org.summaries.mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.fail` } else if (orderBy.field === 'summary-mail-total') { - orgField = aql`org.summaries.mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.total` } else if (orderBy.field === 'summary-web-pass') { - orgField = aql`org.summaries.web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.pass` } else if (orderBy.field === 'summary-web-fail') { - orgField = aql`org.summaries.web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.fail` } else if (orderBy.field === 'summary-web-total') { - orgField = aql`org.summaries.web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.total` } else if (orderBy.field === 'domain-count') { @@ -317,17 +317,17 @@ export const loadOrgConnectionsByDomainId = } else if (orderBy.field === 'verified') { sortByField = aql`org.verified ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-pass') { - sortByField = aql`org.summaries.mail.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-fail') { - sortByField = aql`org.summaries.mail.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-total') { - sortByField = aql`org.summaries.mail.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.total ${orderBy.direction},` } else if (orderBy.field === 'summary-web-pass') { - sortByField = aql`org.summaries.web.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-web-fail') { - sortByField = aql`org.summaries.web.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-web-total') { - sortByField = aql`org.summaries.web.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.total ${orderBy.direction},` } else if (orderBy.field === 'domain-count') { sortByField = aql`COUNT(orgDomains) ${orderBy.direction},` } @@ -402,29 +402,37 @@ export const loadOrgConnectionsByDomainId = search = cleanseInput(search) orgQuery = aql` LET tokenArrEN = TOKENS(${search}, "text_en") - LET searchedOrgsEN = FLATTEN(UNIQUE( + LET searchedOrgsKeysCountEN = FLATTEN( FOR token IN tokenArrEN FOR org IN organizationSearch SEARCH ANALYZER( - org.orgDetails.en.acronym LIKE CONCAT("%", token, "%") - OR org.orgDetails.fr.acronym LIKE CONCAT("%", token, "%") - OR org.orgDetails.en.name LIKE CONCAT("%", token, "%") - OR org.orgDetails.fr.name LIKE CONCAT("%", token, "%") + org.orgDetails.en.acronym LIKE CONCAT("%", token, "%") + OR org.orgDetails.en.name LIKE CONCAT("%", token, "%") , "text_en") FILTER org._key IN orgKeys - RETURN org._key - )) + COLLECT orgKey = org._key WITH COUNT INTO orgCount + RETURN { + 'orgKey': orgKey, + 'orgCount': orgCount + } + ) + LET searchedOrgsEN = searchedOrgsKeysCountEN[* FILTER CURRENT.orgCount == LENGTH(tokenArrEN)].orgKey LET tokenArrFR = TOKENS(${search}, "text_fr") - LET searchedOrgsFR = FLATTEN(UNIQUE( + LET searchedOrgsKeysCountFR = FLATTEN( FOR token IN tokenArrFR - FOR org IN organizationSearch - SEARCH ANALYZER( + FOR org IN organizationSearch + SEARCH ANALYZER( org.orgDetails.fr.acronym LIKE CONCAT("%", token, "%") OR org.orgDetails.fr.name LIKE CONCAT("%", token, "%") - , "text_fr") - FILTER org._key IN orgKeys - RETURN org._key - )) + , "text_fr") + FILTER org._key IN orgKeys + COLLECT orgKey = org._key WITH COUNT INTO orgCount + RETURN { + 'orgKey': orgKey, + 'orgCount': orgCount + } + ) + LET searchedOrgsFR = searchedOrgsKeysCountFR[* FILTER CURRENT.orgCount == LENGTH(tokenArrFR)].orgKey LET searchedOrgs = UNION_DISTINCT(searchedOrgsEN, searchedOrgsFR) ` filterString = aql`FILTER org._key IN searchedOrgs` @@ -458,8 +466,10 @@ export const loadOrgConnectionsByDomainId = _type: "organization", id: org._key, verified: org.verified, + externalId: org.externalId, domainCount: COUNT(orgDomains), - summaries: org.summaries + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE(${language}, org.orgDetails) ) diff --git a/api/src/organization/loaders/load-organization-connections-by-user-id.js b/api/src/organization/loaders/load-organization-connections-by-user-id.js index 96a42cbde..69dbc9a38 100644 --- a/api/src/organization/loaders/load-organization-connections-by-user-id.js +++ b/api/src/organization/loaders/load-organization-connections-by-user-id.js @@ -4,7 +4,21 @@ import { t } from '@lingui/macro' export const loadOrgConnectionsByUserId = ({ query, userKey, cleanseInput, language, i18n, auth: { loginRequiredBool } }) => - async ({ after, before, first, last, orderBy, isSuperAdmin, search, isAdmin, includeSuperAdminOrg, isVerified }) => { + async ({ + after, + before, + first, + last, + orderBy, + isSuperAdmin, + search, + isAdmin, + includeSuperAdminOrg, + isVerified, + isAffiliated, + hasPsd, + hasPgs, + }) => { const userDBId = `users/${userKey}` let afterTemplate = aql`` @@ -52,23 +66,23 @@ export const loadOrgConnectionsByUserId = documentField = aql`afterVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`afterVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`afterVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`afterVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`afterVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`afterVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`afterVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND afterVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -127,23 +141,23 @@ export const loadOrgConnectionsByUserId = documentField = aql`beforeVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`beforeVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`beforeVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`beforeVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`beforeVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`beforeVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`beforeVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND beforeVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -252,27 +266,27 @@ export const loadOrgConnectionsByUserId = hasNextPageDocumentField = aql`LAST(retrievedOrgs).verified` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).verified` } else if (orderBy.field === 'summary-mail-pass') { - orgField = aql`org.summaries.mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - orgField = aql`org.summaries.mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.fail` } else if (orderBy.field === 'summary-mail-total') { - orgField = aql`org.summaries.mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.total` } else if (orderBy.field === 'summary-web-pass') { - orgField = aql`org.summaries.web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.pass` } else if (orderBy.field === 'summary-web-fail') { - orgField = aql`org.summaries.web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.fail` } else if (orderBy.field === 'summary-web-total') { - orgField = aql`org.summaries.web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.total` } else if (orderBy.field === 'domain-count') { @@ -315,17 +329,17 @@ export const loadOrgConnectionsByUserId = } else if (orderBy.field === 'verified') { sortByField = aql`org.verified ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-pass') { - sortByField = aql`org.summaries.mail.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-fail') { - sortByField = aql`org.summaries.mail.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-total') { - sortByField = aql`org.summaries.mail.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.total ${orderBy.direction},` } else if (orderBy.field === 'summary-web-pass') { - sortByField = aql`org.summaries.web.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-web-fail') { - sortByField = aql`org.summaries.web.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-web-total') { - sortByField = aql`org.summaries.web.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.total ${orderBy.direction},` } else if (orderBy.field === 'domain-count') { sortByField = aql`COUNT(orgDomains) ${orderBy.direction},` } @@ -348,6 +362,16 @@ export const loadOrgConnectionsByUserId = isVerifiedQuery = aql`FILTER org.verified == true` } + let hasPsdQuery = aql`` + if (hasPsd) { + hasPsdQuery = aql`FILTER org.policies.psd == true` + } + + let hasPgsQuery = aql`` + if (hasPgs) { + hasPgsQuery = aql`FILTER org.policies.pgs == true` + } + let isAdminFilter = aql`` if (isAdmin) { isAdminFilter = aql`FILTER e.permission IN ["admin", "owner", "super_admin"]` @@ -360,10 +384,30 @@ export const loadOrgConnectionsByUserId = LET orgKeys = ( FOR org IN organizations ${isVerifiedQuery} + ${hasPsdQuery} + ${hasPgsQuery} ${includeSuperAdminOrgQuery} RETURN org._key ) ` + } else if (isAffiliated) { + orgKeysQuery = aql` + WITH affiliations, claims, domains, organizations, organizationSearch, users + LET userAffiliations = ( + FOR v, e IN 1..1 ANY ${userDBId} affiliations + FILTER e.permission != "pending" + RETURN v + ) + LET orgKeys = ( + FOR org IN organizations + ${isVerifiedQuery} + ${hasPsdQuery} + ${hasPgsQuery} + ${includeSuperAdminOrgQuery} + FILTER org._key IN userAffiliations[*]._key + RETURN org._key + ) + ` } else if (!loginRequiredBool) { orgKeysQuery = aql` WITH affiliations, claims, domains, organizations, organizationSearch, users @@ -376,6 +420,8 @@ export const loadOrgConnectionsByUserId = LET orgKeys = ( FOR org IN organizations ${isVerifiedQuery} + ${hasPsdQuery} + ${hasPgsQuery} ${includeSuperAdminOrgQuery} FILTER org._key IN userAffiliations[*]._key ${isAdmin ? aql`` : aql`|| org.verified == true`} RETURN org._key @@ -394,6 +440,8 @@ export const loadOrgConnectionsByUserId = LET orgKeys = ( FOR org IN organizations ${isVerifiedQuery} + ${hasPsdQuery} + ${hasPgsQuery} ${includeSuperAdminOrgQuery} FILTER org._key IN userAffiliations[*]._key ${ isAdmin ? aql`` : aql`|| (org.verified == true && hasVerifiedOrgAffiliation == true)` @@ -410,7 +458,7 @@ export const loadOrgConnectionsByUserId = search = cleanseInput(search) orgQuery = aql` LET tokenArrEN = TOKENS(${search}, "text_en") - LET searchedOrgsEN = FLATTEN(UNIQUE( + LET searchedOrgsKeysCountEN = FLATTEN( FOR token IN tokenArrEN FOR org IN organizationSearch SEARCH ANALYZER( @@ -418,10 +466,15 @@ export const loadOrgConnectionsByUserId = OR org.orgDetails.en.name LIKE CONCAT("%", token, "%") , "text_en") FILTER org._key IN orgKeys - RETURN org._key - )) + COLLECT orgKey = org._key WITH COUNT INTO orgCount + RETURN { + 'orgKey': orgKey, + 'orgCount': orgCount + } + ) + LET searchedOrgsEN = searchedOrgsKeysCountEN[* FILTER CURRENT.orgCount == LENGTH(tokenArrEN)].orgKey LET tokenArrFR = TOKENS(${search}, "text_fr") - LET searchedOrgsFR = FLATTEN(UNIQUE( + LET searchedOrgsKeysCountFR = FLATTEN( FOR token IN tokenArrFR FOR org IN organizationSearch SEARCH ANALYZER( @@ -429,8 +482,13 @@ export const loadOrgConnectionsByUserId = OR org.orgDetails.fr.name LIKE CONCAT("%", token, "%") , "text_fr") FILTER org._key IN orgKeys - RETURN org._key - )) + COLLECT orgKey = org._key WITH COUNT INTO orgCount + RETURN { + 'orgKey': orgKey, + 'orgCount': orgCount + } + ) + LET searchedOrgsFR = searchedOrgsKeysCountFR[* FILTER CURRENT.orgCount == LENGTH(tokenArrFR)].orgKey LET searchedOrgs = UNION_DISTINCT(searchedOrgsEN, searchedOrgsFR) ` filterString = aql`FILTER org._key IN searchedOrgs` @@ -464,8 +522,10 @@ export const loadOrgConnectionsByUserId = _type: "organization", id: org._key, verified: org.verified, + externalId: org.externalId, domainCount: COUNT(orgDomains), - summaries: org.summaries + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE(${language}, org.orgDetails) ) diff --git a/api/src/organization/loaders/load-organization-domain-statuses.js b/api/src/organization/loaders/load-organization-domain-statuses.js index 65b289575..f0009becb 100644 --- a/api/src/organization/loaders/load-organization-domain-statuses.js +++ b/api/src/organization/loaders/load-organization-domain-statuses.js @@ -2,10 +2,11 @@ import { t } from '@lingui/macro' import { aql } from 'arangojs' export const loadOrganizationDomainStatuses = - ({ query, userKey, i18n, language }) => + ({ query, userKey, i18n }) => async ({ orgId, filters }) => { let domains - let domainFilters = aql`FILTER v.archived != true` + let domainFilters = aql`` + let archivedFilter = aql`FILTER v.archived != true` if (typeof filters !== 'undefined') { filters.forEach(({ filterCategory, comparison, filterValue }) => { if (comparison === '==') { @@ -59,12 +60,7 @@ export const loadOrganizationDomainStatuses = FILTER v.status.certificates ${comparison} ${filterValue} ` } else if (filterCategory === 'tags') { - if (filterValue === 'hidden') { - domainFilters = aql` - ${domainFilters} - FILTER e.hidden ${comparison} true - ` - } else if (filterValue === 'nxdomain') { + if (filterValue === 'nxdomain') { domainFilters = aql` ${domainFilters} FILTER v.rcode ${comparison} "NXDOMAIN" @@ -79,12 +75,51 @@ export const loadOrganizationDomainStatuses = ${domainFilters} FILTER v.wildcardSibling ${comparison} true ` + } else if (filterValue === 'wildcard-entry') { + domainFilters = aql` + ${domainFilters} + FILTER v.wildcardEntry ${comparison} true + ` + } else if (filterValue === 'has-entrust-certificate') { + domainFilters = aql` + ${domainFilters} + FILTER v.hasEntrustCertificate ${comparison} true + ` + } else if (filterValue === 'cve-detected') { + domainFilters = aql` + ${domainFilters} + FILTER v.cveDetected ${comparison} true + ` + } else if (filterValue === 'cvd-enrolled') { + domainFilters = aql` + ${domainFilters} + FILTER v.cvdEnrollment.status ${comparison} "enrolled" + ` + } else if (filterValue === 'scan-pending') { + domainFilters = aql`${domainFilters}` + } else if (filterValue === 'archived') { + archivedFilter = aql`FILTER v.archived ${comparison} true` } else { domainFilters = aql` ${domainFilters} - FILTER POSITION(claimTags, ${filterValue}) ${comparison} true + FILTER POSITION(e.tags, ${filterValue}) ${comparison} true ` } + } else if (filterCategory === 'asset-state') { + domainFilters = aql` + ${domainFilters} + FILTER e.assetState ${comparison} ${filterValue} + ` + } else if (filterCategory === 'guidance-tag') { + domainFilters = aql` + ${domainFilters} + FILTER POSITION(negativeTags, ${filterValue}) ${comparison} true + ` + } else if (filterCategory === 'dmarc-phase') { + domainFilters = aql` + ${domainFilters} + FILTER v.phase ${comparison} ${filterValue} + ` } }) } @@ -94,22 +129,41 @@ export const loadOrganizationDomainStatuses = await query` WITH claims, domains, organizations FOR v, e IN 1..1 OUTBOUND ${orgId} claims - LET claimTags = ( - LET translatedTags = ( - FOR tag IN e.tags || [] - RETURN TRANSLATE(${language}, tag) + ${archivedFilter} + LET negativeTags = APPEND(v.negativeTags.dns, v.negativeTags.web) + ${domainFilters} + LET ipAddresses = FIRST( + FILTER v.latestDnsScan + LET latestDnsScan = DOCUMENT(v.latestDnsScan) + FILTER latestDnsScan.resolveIps + RETURN latestDnsScan.resolveIps + ) + LET vulnerabilities = ( + FOR finding IN additionalFindings + FILTER finding.domain == v._id + LIMIT 1 + RETURN UNIQUE( + FOR wc IN finding.webComponents + FILTER LENGTH(wc.WebComponentCves) > 0 + FOR vuln IN wc.WebComponentCves + FILTER vuln.Cve NOT IN (v.ignoredCves || []) + RETURN vuln.Cve ) - RETURN translatedTags )[0] - ${domainFilters} RETURN { domain: v.domain, + ipAddresses: ipAddresses, status: v.status, - tags: claimTags, - hidden: e.hidden, + phase: v.phase, + tags: e.tags, + assetState: e.assetState, rcode: v.rcode, blocked: v.blocked, wildcardSibling: v.wildcardSibling, + wildcardEntry: v.wildcardEntry, + hasEntrustCertificate: v.hasEntrustCertificate, + top25Vulnerabilities: vulnerabilities, + cvdEnrollmentStatus: v.cvdEnrollment.status } ` ).all() diff --git a/api/src/organization/loaders/load-organization-names-by-id.js b/api/src/organization/loaders/load-organization-names-by-id.js new file mode 100644 index 000000000..037ff5a47 --- /dev/null +++ b/api/src/organization/loaders/load-organization-names-by-id.js @@ -0,0 +1,33 @@ +import DataLoader from 'dataloader' +import { t } from '@lingui/macro' + +export const loadOrganizationNamesById = ({ query, userKey, i18n }) => + new DataLoader(async (ids) => { + let cursor + try { + cursor = await query` + FOR orgId IN ${ids} + LET org = DOCUMENT(organizations, orgId) + RETURN { + orgId, + orgNameEN: org.orgDetails.en.name, + orgNameFR: org.orgDetails.fr.name, + } + ` + } catch (err) { + console.error(`Database error occurred when user: ${userKey} running loadOrganizationNamesById: ${err}`) + throw new Error(i18n._(t`Unable to load organization names. Please try again.`)) + } + + const orgMap = {} + try { + await cursor.forEach((org) => { + orgMap[org.orgId] = org + }) + } catch (err) { + console.error(`Cursor error occurred when user: ${userKey} during loadOrganizationNamesById: ${err}`) + throw new Error(i18n._(t`Unable to load organization names. Please try again.`)) + } + + return ids.map((id) => orgMap[id]) + }) diff --git a/api/src/organization/loaders/load-organization-summaries-by-period.js b/api/src/organization/loaders/load-organization-summaries-by-period.js index 08a1d6285..d30cab85b 100644 --- a/api/src/organization/loaders/load-organization-summaries-by-period.js +++ b/api/src/organization/loaders/load-organization-summaries-by-period.js @@ -1,106 +1,48 @@ -import { toGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' +import { aql } from 'arangojs' export const loadOrganizationSummariesByPeriod = ({ query, userKey, cleanseInput, i18n }) => - async ({ orgId, period, year }) => { - if (typeof period === 'undefined') { - console.warn(`User: ${userKey} did not have \`period\` argument set for: loadOrganizationSummariesByPeriod.`) - throw new Error( - i18n._(t`You must provide a \`period\` value to access the \`OrganizationSummaries\` connection.`), - ) - } - const cleansedPeriod = cleanseInput(period) - const monthMap = { - january: '01', - february: '02', - march: '03', - april: '04', - may: '05', - june: '06', - july: '07', - august: '08', - september: '09', - october: '10', - november: '11', - december: '12', + async ({ orgId, startDate, endDate, sortDirection = 'ASC', limit }) => { + const cleansedStartDate = startDate ? cleanseInput(startDate) : null + const cleansedEndDate = endDate ? cleanseInput(endDate) : new Date().toISOString() + + const filterUniqueDates = (array) => { + const filteredArray = [] + const dateSet = new Set() + array.forEach((item) => { + if (!dateSet.has(item.date)) { + filteredArray.push(item) + dateSet.add(item.date) + } + }) + return filteredArray } - const periodMonth = monthMap[cleansedPeriod] - if (typeof year === 'undefined') { - console.warn(`User: ${userKey} did not have \`year\` argument set for: loadOrganizationSummariesByPeriod.`) - throw new Error(i18n._(t`You must provide a \`year\` value to access the \`OrganizationSummaries\` connection.`)) + let startDateFilter = aql`` + if (cleansedStartDate !== null) { + startDateFilter = aql`FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') >= DATE_FORMAT(${cleansedStartDate}, '%yyyy-%mm-%dd')` + } + let endDateFilter = aql`` + if (typeof cleansedEndDate !== 'undefined') { + endDateFilter = aql`FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') <= DATE_FORMAT(${cleansedEndDate}, '%yyyy-%mm-%dd')` + } + let limitString = aql`` + if (typeof limit !== 'undefined') { + limitString = aql`LIMIT ${limit}` } - const periodYear = cleanseInput(year) - let startDate let requestedSummaryInfo try { - if (period === 'thirtyDays') { - startDate = new Date(new Date().setDate(new Date().getDate() - 30)) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN organizationSummaries - FILTER summary.organization == ${orgId} - FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') >= DATE_FORMAT(${startDate}, '%yyyy-%mm-%dd') - SORT summary.date ASC - RETURN summary - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } else if (period === 'lastYear') { - startDate = new Date(new Date().setDate(new Date().getDate() - 365)) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN organizationSummaries - FILTER summary.organization == ${orgId} - FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') >= DATE_FORMAT(${startDate}, '%yyyy-%mm-%dd') - SORT summary.date ASC - RETURN summary - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } else if (period === 'yearToDate') { - startDate = new Date(`${periodYear}-01-01`) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN organizationSummaries - FILTER summary.organization == ${orgId} - FILTER DATE_FORMAT(summary.date, '%yyyy') >= DATE_FORMAT(${startDate}, '%yyyy') - SORT summary.date ASC - RETURN summary - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } else { - startDate = new Date(`${periodYear}-${periodMonth}-01`) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN organizationSummaries - FILTER summary.organization == ${orgId} - FILTER DATE_FORMAT(summary.date, "%yyyy-%mm") == DATE_FORMAT(${startDate}, "%yyyy-%mm") - SORT summary.date ASC - RETURN summary - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } + requestedSummaryInfo = await query` + FOR summary IN organizationSummaries + FILTER summary.organization == ${orgId} + ${startDateFilter} + ${endDateFilter} + SORT summary.date ${sortDirection} + ${limitString} + RETURN summary + ` } catch (err) { console.error( `Database error occurred while user: ${userKey} was trying to gather organization summaries in loadOrganizationSummariesByPeriod, error: ${err}`, @@ -110,7 +52,7 @@ export const loadOrganizationSummariesByPeriod = let summariesInfo try { - summariesInfo = await requestedSummaryInfo.next() + summariesInfo = filterUniqueDates(await requestedSummaryInfo.all()) } catch (err) { console.error( `Cursor error occurred while user: ${userKey} was trying to gather organization summaries in loadOrganizationSummariesByPeriod, error: ${err}`, @@ -118,35 +60,5 @@ export const loadOrganizationSummariesByPeriod = throw new Error(i18n._(t`Unable to load organization summary data. Please try again.`)) } - if (summariesInfo.summaries.length === 0) { - return { - edges: [], - totalCount: 0, - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: '', - endCursor: '', - }, - } - } - - const edges = summariesInfo.summaries.map((summary) => { - summary.startDate = startDate - return { - cursor: toGlobalId('organizationSummary', summary._key), - node: summary, - } - }) - - return { - edges, - totalCount: summariesInfo.totalCount, - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: '', - endCursor: '', - }, - } + return summariesInfo || [] } diff --git a/api/src/organization/mutations/__tests__/archive-organization.test.js b/api/src/organization/mutations/__tests__/archive-organization.test.js index c45c4fd0d..e574daa6f 100644 --- a/api/src/organization/mutations/__tests__/archive-organization.test.js +++ b/api/src/organization/mutations/__tests__/archive-organization.test.js @@ -1,5 +1,6 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -10,7 +11,7 @@ import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -162,6 +163,7 @@ describe('archiving an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -174,8 +176,18 @@ describe('archiving an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -240,8 +252,18 @@ describe('archiving an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -306,17 +328,16 @@ describe('archiving an organization', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn(), verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -373,14 +394,14 @@ describe('archiving an organization', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('admin'), userRequired: jest.fn(), verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _key: 123, verified: true, @@ -407,8 +428,7 @@ describe('archiving an organization', () => { }, }, }), - }, - }, + } } }, }, }) @@ -433,19 +453,8 @@ describe('archiving an organization', () => { }) }) }) - describe('given a trx commit error', () => { + describe('given a data source error', () => { it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValueOnce([]).mockReturnValue([]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({}), - commit: jest.fn().mockRejectedValue(new Error('Commit Error')), - }) - const response = await graphql({ schema, source: ` @@ -473,44 +482,26 @@ describe('archiving an organization', () => { rootValue: null, contextValue: { i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('super_admin'), userRequired: jest.fn(), verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), + dataSources: { + organization: { + byKey: { + load: jest.fn().mockReturnValue({ + _id: 'organizations/123', + _key: 123, + verified: false, + slug: 'treasury-board-secretariat', + name: 'Treasury Board of Canada Secretariat', + }), + }, + archive: jest.fn().mockRejectedValue(new Error('Unable to archive organization. Please try again.')), }, }, }, @@ -519,9 +510,7 @@ describe('archiving an organization', () => { const error = [new GraphQLError('Unable to archive organization. Please try again.')] expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx commit error occurred for user: 123 while attempting archive of org: 123, Error: Commit Error`, - ]) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/__tests__/create-organization.test.js b/api/src/organization/mutations/__tests__/create-organization.test.js index 38cb6b57a..3ab87077a 100644 --- a/api/src/organization/mutations/__tests__/create-organization.test.js +++ b/api/src/organization/mutations/__tests__/create-organization.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -8,16 +9,16 @@ import { createMutationSchema } from '../../../mutation' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput, slugify } from '../../../validators' -import { userRequired, verifiedRequired } from '../../../auth' +import { checkSuperAdmin, superAdminRequired, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgBySlug } from '../../loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' const { DB_PASS: rootPass, DB_URL: url, SIGN_IN_KEY } = process.env describe('create an organization', () => { - let query, drop, truncate, schema, collections, transaction, user + let query, drop, truncate, schema, collections, transaction, user, i18n const consoleOutput = [] const mockedInfo = (output) => consoleOutput.push(output) @@ -32,6 +33,18 @@ describe('create an organization', () => { query: createQuerySchema(), mutation: createMutationSchema(), }) + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) }) afterEach(() => { consoleOutput.length = 0 @@ -73,16 +86,8 @@ describe('create an organization', () => { acronymFR: "SCT" nameEN: "Treasury Board of Canada Secretariat" nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" + externalId: "EXT123" + verified: false } ) { result { @@ -91,12 +96,8 @@ describe('create an organization', () => { acronym slug name - zone - sector - country - province - city verified + externalId } } } @@ -106,6 +107,7 @@ describe('create an organization', () => { contextValue: { request: { language: 'en', + ip: '1.2.3.4', }, query, collections: collectionNames, @@ -117,9 +119,22 @@ describe('create an organization', () => { loadUserByKey: loadUserByKey({ query }), }), verifiedRequired: verifiedRequired({}), + checkSuperAdmin: checkSuperAdmin({ i18n, query, userKey: user._key }), + superAdminRequired: superAdminRequired({ i18n }), + }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), }, loaders: { - loadOrgBySlug: loadOrgBySlug({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, validators: { @@ -145,17 +160,15 @@ describe('create an organization', () => { acronym: org.acronym, slug: org.slug, name: org.name, - zone: org.zone, - sector: org.sector, - country: org.country, - province: org.province, - city: org.city, verified: org.verified, + externalId: org.externalId, }, }, }, } + // externalId is returned as null if not set, not undefined + expectedResponse.data.createOrganization.result.externalId = null expect(response).toEqual(expectedResponse) expect(consoleOutput).toEqual([`User: ${user._key} successfully created a new organization: ${org.slug}`]) }) @@ -172,16 +185,8 @@ describe('create an organization', () => { acronymFR: "SCT" nameEN: "Treasury Board of Canada Secretariat" nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" + externalId: "EXT123" + verified: false } ) { result { @@ -190,12 +195,8 @@ describe('create an organization', () => { acronym slug name - zone - sector - country - province - city verified + externalId } } } @@ -205,6 +206,7 @@ describe('create an organization', () => { contextValue: { request: { language: 'fr', + ip: '1.2.3.4', }, query, collections: collectionNames, @@ -216,9 +218,22 @@ describe('create an organization', () => { loadUserByKey: loadUserByKey({ query }), }), verifiedRequired: verifiedRequired({}), + checkSuperAdmin: checkSuperAdmin({ i18n, query, userKey: user._key }), + superAdminRequired: superAdminRequired({ i18n }), + }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'fr', + cleanseInput, + transaction, + collections: collectionNames, + }), }, loaders: { - loadOrgBySlug: loadOrgBySlug({ query, language: 'fr' }), loadUserByKey: loadUserByKey({ query }), }, validators: { @@ -244,17 +259,15 @@ describe('create an organization', () => { acronym: org.acronym, slug: org.slug, name: org.name, - zone: org.zone, - sector: org.sector, - country: org.country, - province: org.province, - city: org.city, verified: org.verified, + externalId: org.externalId, }, }, }, } + // externalId is returned as null if not set, not undefined + expectedResponse.data.createOrganization.result.externalId = null expect(response).toEqual(expectedResponse) expect(consoleOutput).toEqual([ `User: ${user._key} successfully created a new organization: treasury-board-of-canada-secretariat`, @@ -263,22 +276,7 @@ describe('create an organization', () => { }) }) describe('given an unsuccessful org creation', () => { - let i18n describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) describe('organization already exists', () => { it('returns an error', async () => { const response = await graphql({ @@ -291,16 +289,6 @@ describe('create an organization', () => { acronymFR: "SCT" nameEN: "Treasury Board of Canada Secretariat" nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" } ) { result { @@ -309,11 +297,6 @@ describe('create an organization', () => { acronym slug name - zone - sector - country - province - city verified } ... on OrganizationError { @@ -339,11 +322,11 @@ describe('create an organization', () => { _key: 123, }), verifiedRequired: jest.fn(), + checkSuperAdmin: jest.fn(), + superAdminRequired: jest.fn(), }, + dataSources: { organization: { bySlug: { loadMany: jest.fn().mockReturnValue([{}, undefined]) } } }, loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([{}, undefined]), - }, loadUserByKey: jest.fn(), }, validators: { @@ -370,262 +353,61 @@ describe('create an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when inserting organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - zone - sector - country - province - city - verified - } - ... on OrganizationError { - code - description - } - } + describe('data source error occurs', () => { + it('returns an error', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + createOrganization( + input: { + acronymEN: "TBS" + acronymFR: "SCT" + nameEN: "Treasury Board of Canada Secretariat" + nameFR: "Secrétariat du Conseil Trésor du Canada" } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - }, - }) - - const error = [new GraphQLError('Unable to create organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when user: 123 was creating new organization treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when inserting edge', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" + ) { + result { + ... on Organization { + id + acronym + slug + name + verified } - ) { - result { - ... on Organization { - id - acronym - slug - name - zone - sector - country - province - city - verified - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ next: jest.fn() }) - .mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, + } + `, + rootValue: null, + contextValue: { + i18n, + request: { language: 'en' }, + userKey: 123, + auth: { + userRequired: jest.fn().mockReturnValue({ _key: 123 }), + verifiedRequired: jest.fn(), + checkSuperAdmin: jest.fn(), + superAdminRequired: jest.fn(), }, - }) - - const error = [new GraphQLError('Unable to create organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when inserting edge definition for user: 123 to treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when committing information to db', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - zone - sector - country - province - city - verified - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({ next: jest.fn() }), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, + dataSources: { + organization: { + bySlug: { loadMany: jest.fn().mockReturnValue([undefined, undefined]) }, + create: jest.fn().mockRejectedValue(new Error('Unable to create organization. Please try again.')), }, }, - }) + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Unable to create organization. Please try again.')] + const error = [new GraphQLError('Unable to create organization. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when committing new organization: treasury-board-of-canada-secretariat for user: 123 to db: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) @@ -656,16 +438,6 @@ describe('create an organization', () => { acronymFR: "SCT" nameEN: "Treasury Board of Canada Secretariat" nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" } ) { result { @@ -674,11 +446,6 @@ describe('create an organization', () => { acronym slug name - zone - sector - country - province - city verified } ... on OrganizationError { @@ -704,11 +471,11 @@ describe('create an organization', () => { _key: 123, }), verifiedRequired: jest.fn(), + checkSuperAdmin: jest.fn(), + superAdminRequired: jest.fn(), }, + dataSources: { organization: { bySlug: { loadMany: jest.fn().mockReturnValue([{}, undefined]) } } }, loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([{}, undefined]), - }, loadUserByKey: jest.fn(), }, validators: { @@ -735,262 +502,61 @@ describe('create an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when inserting organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - zone - sector - country - province - city - verified - } - ... on OrganizationError { - code - description - } - } + describe('data source error occurs', () => { + it('returns an error', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + createOrganization( + input: { + acronymEN: "TBS" + acronymFR: "SCT" + nameEN: "Treasury Board of Canada Secretariat" + nameFR: "Secrétariat du Conseil Trésor du Canada" } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - }, - }) - - const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when user: 123 was creating new organization treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when inserting edge', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" + ) { + result { + ... on Organization { + id + acronym + slug + name + verified } - ) { - result { - ... on Organization { - id - acronym - slug - name - zone - sector - country - province - city - verified - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ next: jest.fn() }) - .mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, + } + `, + rootValue: null, + contextValue: { + i18n, + request: { language: 'en' }, + userKey: 123, + auth: { + userRequired: jest.fn().mockReturnValue({ _key: 123 }), + verifiedRequired: jest.fn(), + checkSuperAdmin: jest.fn(), + superAdminRequired: jest.fn(), }, - }) - - const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when inserting edge definition for user: 123 to treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when committing information to db', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - zoneEN: "FED" - zoneFR: "FED" - sectorEN: "TBS" - sectorFR: "TBS" - countryEN: "Canada" - countryFR: "Canada" - provinceEN: "Ontario" - provinceFR: "Ontario" - cityEN: "Ottawa" - cityFR: "Ottawa" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - zone - sector - country - province - city - verified - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({ next: jest.fn() }), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, + dataSources: { + organization: { + bySlug: { loadMany: jest.fn().mockReturnValue([undefined, undefined]) }, + create: jest.fn().mockRejectedValue(new Error('Impossible de créer une organisation. Veuillez réessayer.')), }, }, - }) + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] + const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when committing new organization: treasury-board-of-canada-secretariat for user: 123 to db: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/__tests__/remove-organization.test.js b/api/src/organization/mutations/__tests__/remove-organization.test.js index b349b89d0..7b179c6d8 100644 --- a/api/src/organization/mutations/__tests__/remove-organization.test.js +++ b/api/src/organization/mutations/__tests__/remove-organization.test.js @@ -1,5 +1,6 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -10,7 +11,7 @@ import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -195,6 +196,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -207,8 +209,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -259,6 +272,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -271,8 +285,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -330,8 +355,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -384,6 +420,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -396,8 +433,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -441,6 +489,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -453,8 +502,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -504,6 +564,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -516,8 +577,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -561,6 +633,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -573,8 +646,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -646,8 +730,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -702,8 +797,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -761,8 +867,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -806,6 +923,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -818,8 +936,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -929,6 +1058,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -941,8 +1071,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -993,6 +1134,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1005,8 +1147,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1064,8 +1217,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1118,6 +1282,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1130,8 +1295,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1173,6 +1349,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1185,8 +1362,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1232,6 +1420,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1244,8 +1433,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1289,6 +1489,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1301,8 +1502,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1374,8 +1586,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1430,8 +1653,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1489,8 +1723,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1534,6 +1779,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1546,8 +1792,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1646,6 +1903,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1658,8 +1916,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1710,6 +1979,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1722,8 +1992,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1781,8 +2062,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1835,6 +2127,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1847,8 +2140,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1891,6 +2195,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1903,8 +2208,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1950,6 +2266,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -1962,8 +2279,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2007,6 +2335,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2019,8 +2348,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2092,8 +2432,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2148,8 +2499,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2207,8 +2569,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2252,6 +2625,7 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkPermission: checkPermission({ userKey: user._key, @@ -2264,8 +2638,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2337,17 +2722,16 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn(), userRequired: jest.fn(), verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -2404,14 +2788,14 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('owner'), userRequired: jest.fn(), verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _key: 123, verified: true, @@ -2438,8 +2822,7 @@ describe('removing an organization', () => { }, }, }), - }, - }, + } } }, }, }) @@ -2496,14 +2879,14 @@ describe('removing an organization', () => { collections: collectionNames, transaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('user'), userRequired: jest.fn(), verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _key: 123, verified: false, @@ -2530,8 +2913,7 @@ describe('removing an organization', () => { }, }, }), - }, - }, + } } }, }, }) @@ -2556,936 +2938,8 @@ describe('removing an organization', () => { }) }) }) - describe('given a database error', () => { - describe('when getting the ownership information', () => { - it('throws an error', async () => { - const mockedQuery = jest.fn().mockRejectedValue(new Error('Database Error')) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn(), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred for user: 123 while attempting to get dmarcSummaryInfo while removing org: 123, Error: Database Error`, - ]) - }) - }) - describe('when getting the domain claim count', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValue([]), - } - - const mockedQuery = jest - .fn() - .mockReturnValueOnce(mockedCursor) - .mockRejectedValue(new Error('Database Error')) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn(), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred for user: 123 while attempting to gather domain count while removing org: 123, Error: Database Error`, - ]) - }) - }) - }) - describe('given a cursor error', () => { - describe('when getting getting ownership information', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockRejectedValue(new Error('Cursor Error')), - } - - const mockedQuery = jest - .fn() - .mockReturnValueOnce(mockedCursor) - .mockRejectedValue(new Error('Database Error')) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn(), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred for user: 123 while attempting to get dmarcSummaryInfo while removing org: 123, Error: Cursor Error`, - ]) - }) - }) - describe('when getting getting domain claim count', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValueOnce([]).mockRejectedValue(new Error('Cursor Error')), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn(), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred for user: 123 while attempting to gather domain count while removing org: 123, Error: Cursor Error`, - ]) - }) - }) - }) - describe('given a trx step error', () => { - describe('when removing dmarc summary data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValue([{}]), - } - - const mockedQuery = jest.fn().mockReturnValueOnce(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('Trx Step')), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove dmarc summaries while removing org: 123, Error: Trx Step`, - ]) - }) - }) - describe('when removing ownership data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValue([{}]), - } - - const mockedQuery = jest.fn().mockReturnValueOnce(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValueOnce({}).mockRejectedValue(new Error('Trx Step')), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove ownerships while removing org: 123, Error: Trx Step`, - ]) - }) - }) - describe('when removing web scan results data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1, domain: 'test.gc.ca' }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('Trx Step')), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred while user: 123 attempted to remove web data for test.gc.ca in org: undefined, Error: Trx Step`, - ]) - }) - }) - describe('when removing scan data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1, domain: 'test.gc.ca' }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValueOnce({}).mockRejectedValue(new Error('Trx Step')), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred while user: 123 attempted to remove DNS data for test.gc.ca in org: undefined, error: Error: Trx Step`, - ]) - }) - }) - describe('when removing domain', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1 }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockRejectedValue(new Error('Trx Step')), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove domains while removing org: 123, Error: Trx Step`, - ]) - }) - }) - describe('when removing affiliations and org', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1 }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockRejectedValue(new Error('Trx Step')), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove affiliations, and the org while removing org: 123, Error: Trx Step`, - ]) - }) - }) - }) - describe('given a trx commit error', () => { + describe('given a data source error', () => { it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValueOnce([]).mockReturnValue([]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({}), - commit: jest.fn().mockRejectedValue(new Error('Commit Error')), - }) - const response = await graphql({ schema, source: ` @@ -3513,44 +2967,33 @@ describe('removing an organization', () => { rootValue: null, contextValue: { i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, userKey: 123, + request: { ip: '127.0.0.1' }, auth: { checkPermission: jest.fn().mockReturnValue('owner'), userRequired: jest.fn(), verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: { + byKey: { + load: jest.fn().mockReturnValue({ + _id: 'organizations/123', + _key: '123', + verified: false, + slug: 'treasury-board-secretariat', + name: 'Treasury Board of Canada Secretariat', + }), + }, + getRawByKey: jest.fn().mockResolvedValue({ orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, + en: { name: 'Treasury Board of Canada Secretariat' }, + fr: { name: 'Secrétariat du Conseil Trésor du Canada' }, }, }), + remove: jest.fn().mockRejectedValue(new Error('Unable to remove organization. Please try again.')), }, }, }, @@ -3559,9 +3002,7 @@ describe('removing an organization', () => { const error = [new GraphQLError('Unable to remove organization. Please try again.')] expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx commit error occurred for user: 123 while attempting remove of org: 123, Error: Commit Error`, - ]) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/__tests__/update-organization.test.js b/api/src/organization/mutations/__tests__/update-organization.test.js index 4615f2252..1a169220b 100644 --- a/api/src/organization/mutations/__tests__/update-organization.test.js +++ b/api/src/organization/mutations/__tests__/update-organization.test.js @@ -1,6 +1,5 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { graphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { createQuerySchema } from '../../../query' @@ -8,4111 +7,866 @@ import { createMutationSchema } from '../../../mutation' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput, slugify } from '../../../validators' -import { checkPermission, userRequired, verifiedRequired } from '../../../auth' -import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' -import dbschema from '../../../../database.json' -import { collectionNames } from '../../../collection-names' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('updating an organization', () => { - let query, drop, truncate, schema, collections, transaction, user +const ORG_KEY = 'org123' +const ORG_GID = toGlobalId('organization', ORG_KEY) + +const BASE_RAW_ORG = { + _id: `organizations/${ORG_KEY}`, + _key: ORG_KEY, + externalId: 'ext-001', + externallyManaged: false, + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, +} + +// Flat object returned by byKey.load after the update (language-resolved, as the Organization type expects). +// _type: 'organization' is required by updateOrganizationUnion resolveType. +const RESOLVED_ORG = { + _type: 'organization', + id: ORG_KEY, + _key: ORG_KEY, + _id: `organizations/${ORG_KEY}`, + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + slug: 'treasury-board-secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + verified: false, +} + +const BASE_USER = { _key: 'user123', userName: 'test@example.com' } + +describe('updateOrganization', () => { + let schema, enI18n, frI18n const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) + beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema + console.info = (o) => consoleOutput.push(o) + console.warn = (o) => consoleOutput.push(o) + console.error = (o) => consoleOutput.push(o) + schema = new GraphQLSchema({ query: createQuerySchema(), mutation: createMutationSchema(), }) + + enI18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + + frI18n = setupI18n({ + locale: 'fr', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) }) - beforeEach(() => { + + afterEach(() => { consoleOutput.length = 0 }) - describe('given a successful organization update', () => { - let org - beforeEach(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections, transaction } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - user = await collections.users.save({ - userName: 'test.account@istio.actually.exists', - emailValidated: true, - }) - org = await collections.organizations.save({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, + // Builds the organization data source mock. firstLoad is what byKey.load returns on the + // initial existence check; secondLoad is what it returns after the update + cache clear. + // Note: uses hasOwnProperty so callers can explicitly pass undefined (org not found). + function makeOrgDS(opts = {}) { + const firstLoad = Object.prototype.hasOwnProperty.call(opts, 'firstLoad') ? opts.firstLoad : BASE_RAW_ORG + const secondLoad = opts.secondLoad ?? RESOLVED_ORG + const nameInUseCount = opts.nameInUseCount ?? 0 + const rawOrg = opts.rawOrg ?? BASE_RAW_ORG + const update = opts.update ?? jest.fn() + const checkNameInUse = opts.checkNameInUse ?? null + const getRawByKey = opts.getRawByKey ?? null + + return { + byKey: { + load: jest.fn().mockResolvedValueOnce(firstLoad).mockResolvedValueOnce(secondLoad), + clear: jest.fn(), + }, + checkNameInUse: checkNameInUse ?? jest.fn().mockResolvedValue({ count: nameInUseCount }), + getRawByKey: getRawByKey ?? jest.fn().mockResolvedValue(rawOrg), + update, + } + } + + function makeContext({ + i18n = enI18n, + permission = 'admin', + orgDS = null, + logActivity = jest.fn(), + userKey = 'user123', + ip = '1.2.3.4', + } = {}) { + return { + i18n, + userKey, + request: { ip }, + auth: { + userRequired: jest.fn().mockReturnValue(BASE_USER), + verifiedRequired: jest.fn(), + checkPermission: jest.fn().mockReturnValue(permission), + }, + dataSources: { + auditLogs: { logActivity }, + organization: orgDS ?? makeOrgDS(), + }, + validators: { cleanseInput, slugify }, + } + } + + describe('given a successful update', () => { + it('returns the updated organization on success', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { + id: "${ORG_GID}" + nameEN: "New English Name" + nameFR: "Nouveau Nom Français" + acronymEN: "NEN" + acronymFR: "NNF" + }) { + result { + ... on Organization { id name acronym slug } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext(), }) - }) - - afterEach(async () => { - await truncate() - await drop() - }) - describe('users permission level is super_admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'super_admin', - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toMatchObject({ + name: RESOLVED_ORG.name, + acronym: RESOLVED_ORG.acronym, + slug: RESOLVED_ORG.slug, }) - describe('users language is english', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(consoleOutput).toContain(`User: user123, successfully updated org ${ORG_KEY}.`) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('calls update with correctly merged org details', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameEN: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Updated Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneEN: "New Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + orgKey: ORG_KEY, + updatedOrgDetails: expect.objectContaining({ + orgDetails: expect.objectContaining({ + en: expect.objectContaining({ name: 'Updated Name' }), + }), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'New Zone', - }, - }, - }, - } + it('preserves existing fields when only partial inputs are provided', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorEN: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Only EN Updated" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'New Sector', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryEN: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails.orgDetails.fr.name).toBe(BASE_RAW_ORG.orgDetails.fr.name) + expect(updatedOrgDetails.orgDetails.en.acronym).toBe(BASE_RAW_ORG.orgDetails.en.acronym) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'A New Country', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('clears byKey cache before reloading the organization', async () => { + const clear = jest.fn() + const orgDS = makeOrgDS() + orgDS.byKey.clear = clear - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceEN: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Test" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'A New Province', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(clear).toHaveBeenCalledWith(ORG_KEY) + }) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'A New City', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + describe('audit logging', () => { + it('logs audit activity when nameEN is changed', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Brand New Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NEW_ACRONYM', - name: 'New Name', - zone: 'New Zone', - sector: 'New Sector', - country: 'New Country', - province: 'New Province', - city: 'New City', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ logActivity }), }) - describe('users language is french', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymFR: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + action: 'update', + target: expect.objectContaining({ + resourceType: 'organization', + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ + name: 'nameEN', + oldValue: BASE_RAW_ORG.orgDetails.en.name, + newValue: 'Brand New Name', + }), + ]), + }), + }), + ) + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameFR: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + it('logs audit activity when nameFR is changed', async () => { + const logActivity = jest.fn() - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameFR: "Nouveau Nom" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } } + } + `, + contextValue: makeContext({ logActivity }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneFR: "Secret Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.objectContaining({ + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ name: 'nameFR', newValue: 'Nouveau Nom' }), + ]), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'Secret Zone', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('logs audit activity when acronymEN is changed', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorFR: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" acronymEN: "NEW" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'New Sector', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ logActivity }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryFR: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.objectContaining({ + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ name: 'acronymEN', newValue: 'NEW' }), + ]), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'A New Country', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('logs audit activity when acronymFR is changed', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceFR: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" acronymFR: "NVL" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'A New Province', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ logActivity }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityFR: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.objectContaining({ + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ name: 'acronymFR', newValue: 'NVL' }), + ]), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'A New City', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('does not log when only zone/sector/location fields are updated', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" zoneEN: "NEWZONE" sectorEN: "NEWSECTOR" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NOUVEL_ACRONYME', - city: 'Nouvelle ville', - country: 'Nouveau pays', - name: 'Nouveau nom', - province: 'Nouvelle province', - sector: 'Nouveau secteur', - zone: 'Nouvelle zone', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ logActivity }), }) + + expect(logActivity).not.toHaveBeenCalled() }) - describe('users permission level is admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'admin', - }) - }) - describe('users language is english', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('populates initiatedBy with user key, userName, role, and IP address', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameEN: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Some Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ logActivity, ip: '10.0.0.1', permission: 'admin' }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneEN: "New Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + initiatedBy: { + id: BASE_USER._key, + userName: BASE_USER.userName, + role: 'admin', + ipAddress: '10.0.0.1', + }, + }), + ) + }) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'New Zone', - }, - }, - }, - } + describe('super_admin exclusive fields', () => { + it('super_admin can set externallyManaged to true', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorEN: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externallyManaged: true }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'New Sector', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryEN: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + updatedOrgDetails: expect.objectContaining({ externallyManaged: true }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'A New Country', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('super_admin can set externallyManaged to false', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceEN: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externallyManaged: false }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'A New Province', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + updatedOrgDetails: expect.objectContaining({ externallyManaged: false }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'A New City', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('admin cannot set externallyManaged — field is omitted from update payload', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externallyManaged: true }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NEW_ACRONYM', - name: 'New Name', - zone: 'New Zone', - sector: 'New Sector', - country: 'New Country', - province: 'New Province', - city: 'New City', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ permission: 'admin', orgDS }), }) - describe('users language is french', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymFR: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails).not.toHaveProperty('externallyManaged') + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameFR: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + it('externallyManaged omitted from input is never set even for super_admin', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "No Managed Field" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneFR: "Secret Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails).not.toHaveProperty('externallyManaged') + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'Secret Zone', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('super_admin can update externalId', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorFR: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externalId: "new-ext-id" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'New Sector', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryFR: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + updatedOrgDetails: expect.objectContaining({ externalId: 'new-ext-id' }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'A New Country', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('admin cannot update externalId — field is omitted from update payload', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceFR: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externalId: "should-be-ignored" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'A New Province', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityFR: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails).not.toHaveProperty('externalId') + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'A New City', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('super_admin falls back to existing externalId when none is provided', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Some Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NOUVEL_ACRONYME', - city: 'Nouvelle ville', - country: 'Nouveau pays', - name: 'Nouveau nom', - province: 'Nouvelle province', - sector: 'Nouveau secteur', - zone: 'Nouvelle zone', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), }) + + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails.externalId).toBe(BASE_RAW_ORG.externalId) }) }) - describe('given an unsuccessful organization update', () => { - let i18n - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('organization cannot be found', () => { - describe('organization does not exist in database', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { id: "${toGlobalId('organization', 1)}", cityEN: "A New City" } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: 'Unable to update unknown organization.', - }, - }, - }, + describe('error: unknown organization', () => { + it('returns code 400 with correct message (EN) and logs a warning', async () => { + const orgDS = makeOrgDS({ firstLoad: undefined }) + + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization: 1, however no organizations is associated with that id.`, - ]) - }) - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toEqual({ + code: 400, + description: 'Unable to update unknown organization.', }) - describe('user is located in the database', () => { - describe('user does not have the proper permissions', () => { - describe('user has user level permission', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('user'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(consoleOutput).toContain( + `User: user123 attempted to update organization: ${ORG_KEY}, however no organizations is associated with that id.`, + ) + }) + + it('returns a translated 400 error (FR)', async () => { + const orgDS = makeOrgDS({ firstLoad: undefined }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - 'Permission Denied: Please contact organization admin for help with updating organization.', - }, - }, - }, + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } } + } + } + `, + contextValue: makeContext({ i18n: frI18n, orgDS }), + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: user`, - ]) - }) - }) - describe('user does not belong to that organization', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue(undefined), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(response.data.updateOrganization.result.code).toBe(400) + expect(response.data.updateOrganization.result.description).not.toBe('Unable to update unknown organization.') + }) + }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - 'Permission Denied: Please contact organization admin for help with updating organization.', - }, - }, - }, + describe('error: insufficient permission', () => { + it('returns code 403 with correct message for user role (EN) and logs an error', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } } + } + } + `, + contextValue: makeContext({ permission: 'user' }), + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: undefined`, - ]) - }) - }) - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toEqual({ + code: 403, + description: 'Permission Denied: Please contact organization admin for help with updating organization.', }) - describe('organization name is already in use', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ count: 1 }), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(consoleOutput).toContain( + `User: user123 attempted to update organization ${ORG_KEY}, however they do not have the correct permission level. Permission: user`, + ) + }) - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: 'Organization name already in use, please choose another and try again.', - }, - }, - }, + it('returns a translated 403 error for user role (FR)', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to change the name of org: 123 however it is already in use.`, - ]) - }) + `, + contextValue: makeContext({ i18n: frI18n, permission: 'user' }), }) - describe('cursor error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next() { - throw new Error('Database error occurred.') - }, - }), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = [new GraphQLError('Unable to update organization. Please try again.')] + expect(response.data.updateOrganization.result.code).toBe(403) + expect(response.data.updateOrganization.result.description).not.toBe( + 'Permission Denied: Please contact organization admin for help with updating organization.', + ) + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) + it('returns code 403 for undefined permission (no org affiliation)', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } + } + `, + contextValue: makeContext({ permission: null }), }) - describe('database error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to update organization. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) - describe('when checking to see if orgName is already in use', () => { - it('throws an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(response.data.updateOrganization.result.code).toBe(403) + }) + }) - const error = [new GraphQLError('Unable to update organization. Please try again.')] + describe('error: organization name already in use', () => { + it('returns code 400 with correct message (EN) and logs an error', async () => { + const orgDS = makeOrgDS({ nameInUseCount: 1 }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred during name check when user: 123 attempted to update org: 123, Error: Database error occurred.`, - ]) - }) - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Taken Name" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } + } + `, + contextValue: makeContext({ orgDS }), }) - describe('transaction error occurs', () => { - describe('when updating/inserting new org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to update organization. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when committing transaction', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to update organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing org: 123, err: Error: trx commit error`, - ]) - }) - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toEqual({ + code: 400, + description: 'Organization name already in use, please choose another and try again.', }) + expect(consoleOutput).toContain( + `User: user123 attempted to change the name of org: ${ORG_KEY} however it is already in use.`, + ) }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('organization cannot be found', () => { - describe('organization does not exist in database', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { id: "${toGlobalId('organization', 1)}", cityEN: "A New City" } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: 'Impossible de mettre à jour une organisation inconnue.', - }, - }, - }, - } + it('returns a translated 400 error when name conflicts (FR)', async () => { + const orgDS = makeOrgDS({ nameInUseCount: 1 }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization: 1, however no organizations is associated with that id.`, - ]) - }) - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Taken Name" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } + } + `, + contextValue: makeContext({ i18n: frI18n, orgDS }), }) - describe('user is located in the database', () => { - describe('user does not have the proper permissions', () => { - describe('user has user level permission', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('user'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.", - }, - }, - }, - } + expect(response.data.updateOrganization.result.code).toBe(400) + expect(response.data.updateOrganization.result.description).not.toBe( + 'Organization name already in use, please choose another and try again.', + ) + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: user`, - ]) - }) - }) - describe('user does not belong to that organization', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue(undefined), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + it('skips the name check when neither nameEN nor nameFR is provided', async () => { + const checkNameInUse = jest.fn() + const orgDS = makeOrgDS({ checkNameInUse }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.", - }, - }, - }, + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" zoneEN: "NewZone" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: undefined`, - ]) - }) - }) - }) - }) - describe('organization name is already in use', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ count: 1 }), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: "Le nom de l'organisation est déjà utilisé, veuillez en choisir un autre et réessayer.", - }, - }, - }, + } } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to change the name of org: 123 however it is already in use.`, - ]) - }) + `, + contextValue: makeContext({ orgDS }), }) - describe('cursor error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next() { - throw new Error('Database error occurred.') - }, - }), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + expect(checkNameInUse).not.toHaveBeenCalled() + }) + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) + describe('error: data source failures', () => { + it('propagates error thrown by organizationDS.update', async () => { + const orgDS = makeOrgDS({ + update: jest.fn().mockRejectedValue(new Error('Unable to load organization. Please try again.')), }) - describe('database error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) - describe('when checking to see if orgName is already in use', () => { - it('throws an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Fail Update" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext({ orgDS }), + }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + expect(response.errors).toBeDefined() + expect(response.errors[0].message).toBe('Unable to load organization. Please try again.') + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred during name check when user: 123 attempted to update org: 123, Error: Database error occurred.`, - ]) - }) - }) + it('propagates error thrown by organizationDS.checkNameInUse', async () => { + const orgDS = makeOrgDS({ + checkNameInUse: jest.fn().mockRejectedValue(new Error('Unable to update organization. Please try again.')), }) - describe('transaction error occurs', () => { - describe('when updating/inserting new org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Fail Check" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when committing transaction', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(response.errors).toBeDefined() + expect(response.errors[0].message).toBe('Unable to update organization. Please try again.') + }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + it('propagates error thrown by organizationDS.getRawByKey', async () => { + const orgDS = makeOrgDS({ + getRawByKey: jest.fn().mockRejectedValue(new Error('Unable to load organization. Please try again.')), + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing org: 123, err: Error: trx commit error`, - ]) - }) - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" zoneEN: "FED" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext({ orgDS }), }) + + expect(response.errors).toBeDefined() + expect(response.errors[0].message).toBe('Unable to load organization. Please try again.') }) }) }) diff --git a/api/src/organization/mutations/__tests__/verify-organization.test.js b/api/src/organization/mutations/__tests__/verify-organization.test.js index 96b8c8908..86a37edfa 100644 --- a/api/src/organization/mutations/__tests__/verify-organization.test.js +++ b/api/src/organization/mutations/__tests__/verify-organization.test.js @@ -1,5 +1,6 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -10,8 +11,8 @@ import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' import { loadDomainByKey } from '../../../domain/loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -171,13 +172,18 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: loadOrgByKey({ + dataSources: { + organization: new OrganizationDataSource({ query, - language: 'en', userKey: user._key, i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, }), + }, + loaders: { loadUserByKey: loadUserByKey({ query, userKey: user._key, @@ -203,13 +209,8 @@ describe('removing an organization', () => { expect(response).toEqual(expectedResponse) expect(consoleOutput).toEqual([`User: ${user._key}, successfully verified org: ${org._key}.`]) - const orgLoader = loadOrgByKey({ - query, - language: 'en', - userKey: user._key, - i18n, - }) - const verifiedOrg = await orgLoader.load(org._key) + const orgDS = new OrganizationDataSource({ query, userKey: user._key, i18n, language: 'en', cleanseInput, transaction, collections: collectionNames }) + const verifiedOrg = await orgDS.byKey.load(org._key) expect(verifiedOrg.verified).toEqual(true) const domainLoader = loadDomainByKey({ query, userKey: user._key, i18n }) @@ -285,13 +286,18 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: loadOrgByKey({ + dataSources: { + organization: new OrganizationDataSource({ query, - language: 'fr', userKey: user._key, i18n, + language: 'fr', + cleanseInput, + transaction, + collections: collectionNames, }), + }, + loaders: { loadUserByKey: loadUserByKey({ query, userKey: user._key, @@ -317,13 +323,8 @@ describe('removing an organization', () => { expect(response).toEqual(expectedResponse) expect(consoleOutput).toEqual([`User: ${user._key}, successfully verified org: ${org._key}.`]) - const orgLoader = loadOrgByKey({ - query, - language: 'fr', - userKey: user._key, - i18n, - }) - const verifiedOrg = await orgLoader.load(org._key) + const orgDS = new OrganizationDataSource({ query, userKey: user._key, i18n, language: 'fr', cleanseInput, transaction, collections: collectionNames }) + const verifiedOrg = await orgDS.byKey.load(org._key) expect(verifiedOrg.verified).toEqual(true) }) }) @@ -384,11 +385,9 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -449,13 +448,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -516,13 +513,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -584,13 +579,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ verified: true, }), - }, - }, + } } }, }, }) @@ -611,189 +604,55 @@ describe('removing an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when stepping transaction', () => { - describe('when upserting org information', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } + describe('data source error occurs', () => { + it('throws an error message', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + verifyOrganization( + input: { + orgId: "${toGlobalId('organization', 123)}" } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to verify organization. Please try again.')] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when clearing owners', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ) { + result { + ... on OrganizationResult { + status + organization { + name } } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to verify organization. Please try again.')] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - }) - describe('when committing transaction', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, + } + `, + rootValue: null, + contextValue: { + i18n, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('super_admin'), + userRequired: jest.fn(), + verifiedRequired: jest.fn(), + }, + validators: { cleanseInput }, + dataSources: { + organization: { + byKey: { load: jest.fn().mockReturnValue({ verified: false, _key: 123 }) }, + verify: jest.fn().mockRejectedValue(new Error('Unable to verify organization. Please try again.')), }, }, - }) - - const error = [new GraphQLError('Unable to verify organization. Please try again.')] + }, + }) - expect(response.errors).toEqual(error) + const error = [new GraphQLError('Unable to verify organization. Please try again.')] - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing newly verified org: 123, err: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) @@ -851,11 +710,9 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -916,13 +773,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -983,13 +838,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -1051,13 +904,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ verified: true, }), - }, - }, + } } }, }, }) @@ -1078,189 +929,55 @@ describe('removing an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when stepping transaction', () => { - describe('when upserting org information', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } + describe('data source error occurs', () => { + it('throws an error message', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + verifyOrganization( + input: { + orgId: "${toGlobalId('organization', 123)}" } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when clearing owners', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ) { + result { + ... on OrganizationResult { + status + organization { + name } } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - }) - describe('when committing transaction', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, + } + `, + rootValue: null, + contextValue: { + i18n, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('super_admin'), + userRequired: jest.fn(), + verifiedRequired: jest.fn(), + }, + validators: { cleanseInput }, + dataSources: { + organization: { + byKey: { load: jest.fn().mockReturnValue({ verified: false, _key: 123 }) }, + verify: jest.fn().mockRejectedValue(new Error("Impossible de vérifier l'organisation. Veuillez réessayer.")), }, }, - }) - - const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] + }, + }) - expect(response.errors).toEqual(error) + const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing newly verified org: 123, err: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/archive-organization.js b/api/src/organization/mutations/archive-organization.js index 774ab9eab..583fa0de5 100644 --- a/api/src/organization/mutations/archive-organization.js +++ b/api/src/organization/mutations/archive-organization.js @@ -3,7 +3,7 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { removeOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' +import ac from '../../access-control' export const archiveOrganization = new mutationWithClientMutationId({ name: 'ArchiveOrganization', @@ -25,13 +25,11 @@ export const archiveOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired }, validators: { cleanseInput }, - loaders: { loadOrgByKey }, + dataSources: { auditLogs, organization: organizationDS }, }, ) => { // Get user @@ -43,7 +41,7 @@ export const archiveOrganization = new mutationWithClientMutationId({ const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) // Get org from db - const organization = await loadOrgByKey.load(orgId) + const organization = await organizationDS.byKey.load(orgId) // Check to see if org exists if (!organization) { @@ -58,7 +56,7 @@ export const archiveOrganization = new mutationWithClientMutationId({ // Get users permission const permission = await checkPermission({ orgId: organization._id }) - if (permission !== 'super_admin') { + if (!ac.can(permission).deleteAny('organization').granted) { console.warn( `User: ${userKey} attempted to archive org: ${organization._key}, however they do not have the correct permission level. Permission: ${permission}`, ) @@ -69,102 +67,16 @@ export const archiveOrganization = new mutationWithClientMutationId({ } } - // Setup Trans action - const trx = await transaction(collections) - - // check to see if any other orgs are using this domain - let countCursor - try { - countCursor = await query` - WITH claims, domains, organizations - LET domainIds = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} claims - RETURN e._to - ) - FOR domain IN domains - FILTER domain._id IN domainIds - LET count = LENGTH( - FOR v, e IN 1..1 INBOUND domain._id claims - RETURN 1 - ) - RETURN { - _id: domain._id, - _key: domain._key, - domain: domain.domain, - count - } - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey} while attempting to gather domain count while archiving org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - - let domainInfo - try { - domainInfo = await countCursor.all() - } catch (err) { - console.error( - `Cursor error occurred for user: ${userKey} while attempting to gather domain count while archiving org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - - for (const domain of domainInfo) { - if (domain.count === 1) { - try { - // Archive domain - await trx.step( - async () => - await query` - WITH domains - UPDATE { _key: ${domain._key}, archived: true } IN domains - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to archive domains while archiving org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - } - } - - try { - await trx.step( - () => - query` - WITH organizations - UPDATE { _key: ${organization._key}, verified: false } IN organizations - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to unverify while archiving org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error( - `Trx commit error occurred for user: ${userKey} while attempting archive of org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } + await organizationDS.archive({ organization }) console.info(`User: ${userKey} successfully archived org: ${organization._key}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'update', updatedProperties: [ @@ -178,8 +90,8 @@ export const archiveOrganization = new mutationWithClientMutationId({ resource: { en: organization.name, fr: organization.name, - }, // name of resource being acted upon - resourceType: 'organization', // user, org, domain + }, + resourceType: 'organization', }, }) diff --git a/api/src/organization/mutations/create-organization.js b/api/src/organization/mutations/create-organization.js index 6586a64c1..10c2d638e 100644 --- a/api/src/organization/mutations/create-organization.js +++ b/api/src/organization/mutations/create-organization.js @@ -1,10 +1,9 @@ -import { GraphQLNonNull, GraphQLString } from 'graphql' +import { GraphQLNonNull, GraphQLString, GraphQLBoolean } from 'graphql' import { mutationWithClientMutationId } from 'graphql-relay' import { t } from '@lingui/macro' import { Acronym } from '../../scalars' import { createOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' export const createOrganization = new mutationWithClientMutationId({ name: 'CreateOrganization', @@ -26,45 +25,13 @@ export const createOrganization = new mutationWithClientMutationId({ type: new GraphQLNonNull(GraphQLString), description: 'The French name of the organization.', }, - zoneEN: { - type: new GraphQLNonNull(GraphQLString), - description: 'The English translation of the zone the organization belongs to.', - }, - zoneFR: { - type: new GraphQLNonNull(GraphQLString), - description: 'The English translation of the zone the organization belongs to.', - }, - sectorEN: { - type: new GraphQLNonNull(GraphQLString), - description: 'The English translation of the sector the organization belongs to.', - }, - sectorFR: { - type: new GraphQLNonNull(GraphQLString), - description: 'The French translation of the sector the organization belongs to.', - }, - countryEN: { - type: new GraphQLNonNull(GraphQLString), - description: 'The English translation of the country the organization resides in.', - }, - countryFR: { - type: new GraphQLNonNull(GraphQLString), - description: 'The French translation of the country the organization resides in.', - }, - provinceEN: { - type: new GraphQLNonNull(GraphQLString), - description: 'The English translation of the province the organization resides in.', - }, - provinceFR: { - type: new GraphQLNonNull(GraphQLString), - description: 'The French translation of the province the organization resides in.', - }, - cityEN: { - type: new GraphQLNonNull(GraphQLString), - description: 'The English translation of the city the organization resides in.', + externalId: { + type: GraphQLString, + description: 'String ID used to identify the organization in an external system.', }, - cityFR: { - type: new GraphQLNonNull(GraphQLString), - description: 'The French translation of the city the organization resides in.', + verified: { + type: GraphQLBoolean, + description: 'If the organization is verified.', }, }), outputFields: () => ({ @@ -79,12 +46,10 @@ export const createOrganization = new mutationWithClientMutationId({ { i18n, request, - collections, - transaction, - query, userKey, - auth: { userRequired, verifiedRequired }, - loaders: { loadOrgBySlug }, + request: { ip }, + auth: { userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired }, + dataSources: { auditLogs, organization: organizationDS }, validators: { cleanseInput, slugify }, }, ) => { @@ -92,29 +57,25 @@ export const createOrganization = new mutationWithClientMutationId({ const user = await userRequired() verifiedRequired({ user }) + const isSuperAdmin = await checkSuperAdmin() + + if (args.verified === true) { + superAdminRequired({ user, isSuperAdmin }) + } // Cleanse Input const acronymEN = cleanseInput(args.acronymEN) const acronymFR = cleanseInput(args.acronymFR) const nameEN = cleanseInput(args.nameEN) const nameFR = cleanseInput(args.nameFR) - const zoneEN = cleanseInput(args.zoneEN) - const zoneFR = cleanseInput(args.zoneFR) - const sectorEN = cleanseInput(args.sectorEN) - const sectorFR = cleanseInput(args.sectorFR) - const countryEN = cleanseInput(args.countryEN) - const countryFR = cleanseInput(args.countryFR) - const provinceEN = cleanseInput(args.provinceEN) - const provinceFR = cleanseInput(args.provinceFR) - const cityEN = cleanseInput(args.cityEN) - const cityFR = cleanseInput(args.cityFR) + const externalId = cleanseInput(args.externalId) // Create EN and FR slugs const slugEN = slugify(nameEN) const slugFR = slugify(nameFR) // Check to see if org already exists - const [orgEN, orgFR] = await loadOrgBySlug.loadMany([slugEN, slugFR]) + const [orgEN, orgFR] = await organizationDS.bySlug.loadMany([slugEN, slugFR]) if (typeof orgEN !== 'undefined' || typeof orgFR !== 'undefined') { console.warn(`User: ${userKey} attempted to create an organization that already exists: ${slugEN}`) @@ -125,109 +86,44 @@ export const createOrganization = new mutationWithClientMutationId({ } } - // Create new organization const organizationDetails = { - verified: false, + verified: args.verified || false, externallyManaged: false, + externalId, orgDetails: { en: { slug: slugEN, acronym: acronymEN, name: nameEN, - zone: zoneEN, - sector: sectorEN, - country: countryEN, - province: provinceEN, - city: cityEN, }, fr: { slug: slugFR, acronym: acronymFR, name: nameFR, - zone: zoneFR, - sector: sectorFR, - country: countryFR, - province: provinceFR, - city: cityFR, }, }, } - // Setup Trans action - const trx = await transaction(collections) - - let cursor - try { - cursor = await trx.step( - () => - query` - WITH organizations - INSERT ${organizationDetails} INTO organizations - RETURN MERGE( - { - _id: NEW._id, - _key: NEW._key, - _rev: NEW._rev, - _type: "organization", - id: NEW._key, - verified: NEW.verified, - domainCount: 0, - summaries: NEW.summaries - }, - TRANSLATE(${request.language}, NEW.orgDetails) - ) - `, - ) - } catch (err) { - console.error(`Transaction error occurred when user: ${userKey} was creating new organization ${slugEN}: ${err}`) - throw new Error(i18n._(t`Unable to create organization. Please try again.`)) - } - const organization = await cursor.next() - - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - INSERT { - _from: ${organization._id}, - _to: ${user._id}, - permission: "owner", - } INTO affiliations - `, - ) - } catch (err) { - console.error( - `Transaction error occurred when inserting edge definition for user: ${userKey} to ${slugEN}: ${err}`, - ) - throw new Error(i18n._(t`Unable to create organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction error occurred when committing new organization: ${slugEN} for user: ${userKey} to db: ${err}`, - ) - throw new Error(i18n._(t`Unable to create organization. Please try again.`)) - } + const organization = await organizationDS.create({ + organizationDetails, + userId: user._id, + language: request.language, + }) console.info(`User: ${userKey} successfully created a new organization: ${slugEN}`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, + ipAddress: ip, }, action: 'create', target: { resource: { en: organizationDetails.orgDetails.en.name, fr: organizationDetails.orgDetails.fr.name, - }, // name of resource being acted upon - resourceType: 'organization', // user, org, domain + }, + resourceType: 'organization', }, }) diff --git a/api/src/organization/mutations/remove-organization.js b/api/src/organization/mutations/remove-organization.js index 17aa9c7e8..114566b58 100644 --- a/api/src/organization/mutations/remove-organization.js +++ b/api/src/organization/mutations/remove-organization.js @@ -3,7 +3,7 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { removeOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' +import ac from '../../access-control' export const removeOrganization = new mutationWithClientMutationId({ name: 'RemoveOrganization', @@ -25,13 +25,11 @@ export const removeOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired }, validators: { cleanseInput }, - loaders: { loadOrgByKey }, + dataSources: { auditLogs, organization: organizationDS }, }, ) => { // Get user @@ -43,7 +41,7 @@ export const removeOrganization = new mutationWithClientMutationId({ const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) // Get org from db - const organization = await loadOrgByKey.load(orgId) + const organization = await organizationDS.byKey.load(orgId) // Check to see if org exists if (!organization) { @@ -58,7 +56,7 @@ export const removeOrganization = new mutationWithClientMutationId({ // Get users permission const permission = await checkPermission({ orgId: organization._id }) - if (['owner', 'super_admin'].includes(permission) === false) { + if (!ac.can(permission).deleteOwn('organization').granted) { console.warn( `User: ${userKey} attempted to remove org: ${organization._key}, however the user does not have permission to this organization.`, ) @@ -72,7 +70,7 @@ export const removeOrganization = new mutationWithClientMutationId({ } // Check to see if org is verified check, and the user is super admin - if (organization.verified && permission !== 'super_admin') { + if (organization.verified && !ac.can(permission).deleteAny('organization').granted) { console.warn( `User: ${userKey} attempted to remove org: ${organization._key}, however the user is not a super admin.`, ) @@ -83,318 +81,24 @@ export const removeOrganization = new mutationWithClientMutationId({ } } - // Setup Trans action - const trx = await transaction(collections) - - // check to see if org has any dmarc summaries - let dmarcSummaryCheckCursor - try { - dmarcSummaryCheckCursor = await query` - WITH domains, ownership, dmarcSummaries, organizations - FOR v, e IN 1..1 OUTBOUND ${organization._id} ownership - RETURN e - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey} while attempting to get dmarcSummaryInfo while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - let dmarcSummaryCheckList - try { - dmarcSummaryCheckList = await dmarcSummaryCheckCursor.all() - } catch (err) { - console.error( - `Cursor error occurred for user: ${userKey} while attempting to get dmarcSummaryInfo while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - for (const ownership of dmarcSummaryCheckList) { - try { - await trx.step( - () => query` - WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries - LET dmarcSummaryEdges = ( - FOR v, e IN 1..1 OUTBOUND ${ownership._to} domainsToDmarcSummaries - RETURN { edgeKey: e._key, dmarcSummaryId: e._to } - ) - LET removeDmarcSummaryEdges = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries - OPTIONS { waitForSync: true } - ) - LET removeDmarcSummary = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key - REMOVE key IN dmarcSummaries - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove dmarc summaries while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - await trx.step( - () => query` - WITH ownership, organizations, domains - REMOVE ${ownership._key} IN ownership - OPTIONS { waitForSync: true } - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove ownerships while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - } - - // check to see if any other orgs are using this domain - let countCursor - try { - countCursor = await query` - WITH claims, domains, organizations - LET domainIds = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} claims - RETURN e._to - ) - FOR domain IN domains - FILTER domain._id IN domainIds - LET count = LENGTH( - FOR v, e IN 1..1 INBOUND domain._id claims - RETURN 1 - ) - RETURN { - "_id": domain._id, - "_key": domain._key, - "domain": domain.domain, - "count": count - } - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey} while attempting to gather domain count while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - let domainInfo - try { - domainInfo = await countCursor.all() - } catch (err) { - console.error( - `Cursor error occurred for user: ${userKey} while attempting to gather domain count while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - for (const domain of domainInfo) { - if (domain.count === 1) { - try { - // Remove web data - await trx.step(async () => { - await query` - WITH web, webScan - FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb - LET removeWebScansQuery = ( - FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans - REMOVE webScanV IN webScan - REMOVE webToWebScansV IN webToWebScans - OPTIONS { waitForSync: true } - ) - REMOVE webV IN web - REMOVE domainsWebEdge IN domainsWeb - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove web data for ${domain.domain} in org: ${organization.slug}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - // Remove DNS data - await trx.step(async () => { - await query` - WITH dns - FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS - REMOVE dnsV IN dns - REMOVE domainsDNSEdge IN domainsDNS - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DNS data for ${domain.domain} in org: ${organization.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - // remove favourites - try { - await trx.step(async () => { - await query` - WITH favourites, domains - FOR fav IN favourites - FILTER fav._to == ${domain._id} - REMOVE fav IN favourites - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove favourites for ${domain.domain} in org: ${organization.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - // remove DKIM selectors - try { - await trx.step(async () => { - await query` - FOR e IN domainsToSelectors - FILTER e._from == ${domain._id} - REMOVE e IN domainsToSelectors - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DKIM selectors for ${domain.domain} in org: ${organization.slug}, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - // Remove domain - await trx.step( - () => - query` - WITH claims, domains, organizations - LET domainEdges = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} claims - FILTER e._to == ${domain._id} - RETURN { edgeKey: e._key, domainId: e._to } - ) - LET removeDomainEdges = ( - FOR domainEdge in domainEdges - REMOVE domainEdge.edgeKey IN claims - OPTIONS { waitForSync: true } - ) - LET removeDomain = ( - FOR domainEdge in domainEdges - LET key = PARSE_IDENTIFIER(domainEdge.domainId).key - REMOVE key IN domains - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove domains while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - } - } - - let orgCursor - let compareOrg - if (typeof organization !== 'undefined') { - // Get all org details for comparison - try { - orgCursor = await query` - WITH organizations - FOR org IN organizations - FILTER org._key == ${organization._key} - RETURN org - ` - } catch (err) {} - - try { - compareOrg = await orgCursor.next() - } catch (err) {} - } - - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - LET userEdges = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} affiliations - RETURN { edgeKey: e._key, userKey: e._to } - ) - LET removeUserEdges = ( - FOR userEdge IN userEdges - REMOVE userEdge.edgeKey IN affiliations - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - - await trx.step( - () => - query` - WITH organizations, organizationSummaries - FOR summary in organizationSummaries - FILTER summary.organization == ${organization._id} - REMOVE summary._key IN organizationSummaries - OPTIONS { waitForSync: true } - `, - ) - - await trx.step( - () => - query` - WITH organizations - REMOVE ${organization._key} IN organizations - OPTIONS { waitForSync: true } - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove affiliations, and the org while removing org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error( - `Trx commit error occurred for user: ${userKey} while attempting remove of org: ${organization._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } + const compareOrg = await organizationDS.getRawByKey({ orgKey: organization._key }) + await organizationDS.remove({ organization }) console.info(`User: ${userKey} successfully removed org: ${organization._key}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'delete', target: { resource: { - en: compareOrg.orgDetails.en.name || organization.name, - fr: compareOrg.orgDetails.fr.name || organization.name, - }, // name of resource being acted upon - resourceType: 'organization', // user, org, domain + en: compareOrg?.orgDetails.en.name || organization.name, + fr: compareOrg?.orgDetails.fr.name || organization.name, + }, + resourceType: 'organization', }, }) diff --git a/api/src/organization/mutations/update-organization.js b/api/src/organization/mutations/update-organization.js index ca00ea0b0..95073f17c 100644 --- a/api/src/organization/mutations/update-organization.js +++ b/api/src/organization/mutations/update-organization.js @@ -4,7 +4,7 @@ import { t } from '@lingui/macro' import { Acronym } from '../../scalars' import { updateOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' +import ac from '../../access-control' export const updateOrganization = new mutationWithClientMutationId({ name: 'UpdateOrganization', @@ -74,6 +74,18 @@ export const updateOrganization = new mutationWithClientMutationId({ type: GraphQLBoolean, description: 'If the organization has domains that are managed externally.', }, + externalId: { + type: GraphQLString, + description: 'String ID used to identify the organization in an external system.', + }, + psd: { + type: GraphQLBoolean, + description: 'Whether the Policy on Service and Digital applies to this organization.', + }, + pgs: { + type: GraphQLBoolean, + description: 'Whether the Policy on Government Security applies to this organization.', + }, }), outputFields: () => ({ result: { @@ -86,12 +98,10 @@ export const updateOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, + request: { ip }, auth: { checkPermission, userRequired, verifiedRequired }, - loaders: { loadOrgByKey }, + dataSources: { auditLogs, organization: organizationDS }, validators: { cleanseInput, slugify }, }, ) => { @@ -116,13 +126,14 @@ export const updateOrganization = new mutationWithClientMutationId({ const provinceFR = cleanseInput(args.provinceFR) const cityEN = cleanseInput(args.cityEN) const cityFR = cleanseInput(args.cityFR) + const externalId = cleanseInput(args.externalId) // Create Slug const slugEN = slugify(nameEN) const slugFR = slugify(nameFR) // Check to see if org exists - const currentOrg = await loadOrgByKey.load(orgKey) + const currentOrg = await organizationDS.byKey.load(orgKey) if (typeof currentOrg === 'undefined') { console.warn( @@ -138,7 +149,7 @@ export const updateOrganization = new mutationWithClientMutationId({ // Check to see if user has permission const permission = await checkPermission({ orgId: currentOrg._id }) - if (permission !== 'admin' && permission !== 'super_admin') { + if (!ac.can(permission).updateOwn('organization').granted) { console.error( `User: ${userKey} attempted to update organization ${orgKey}, however they do not have the correct permission level. Permission: ${permission}`, ) @@ -153,21 +164,7 @@ export const updateOrganization = new mutationWithClientMutationId({ // Check to see if any orgs already have the name in use if (nameEN !== '' || nameFR !== '') { - let orgNameCheckCursor - try { - orgNameCheckCursor = await query` - WITH organizations - FOR org IN organizations - FILTER (org.orgDetails.en.name == ${nameEN}) OR (org.orgDetails.fr.name == ${nameFR}) - RETURN org - ` - } catch (err) { - console.error( - `Database error occurred during name check when user: ${userKey} attempted to update org: ${currentOrg._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } - + const orgNameCheckCursor = await organizationDS.checkNameInUse({ nameEN, nameFR }) if (orgNameCheckCursor.count > 0) { console.error( `User: ${userKey} attempted to change the name of org: ${currentOrg._key} however it is already in use.`, @@ -180,27 +177,7 @@ export const updateOrganization = new mutationWithClientMutationId({ } } - // Get all org details for comparison - let orgCursor - try { - orgCursor = await query` - WITH organizations - FOR org IN organizations - FILTER org._key == ${orgKey} - RETURN org - ` - } catch (err) { - console.error(`Database error occurred while retrieving org: ${orgKey} for update, err: ${err}`) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } - - let compareOrg - try { - compareOrg = await orgCursor.next() - } catch (err) { - console.error(`Cursor error occurred while retrieving org: ${orgKey} for update, err: ${err}`) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } + const compareOrg = await organizationDS.getRawByKey({ orgKey }) const updatedOrgDetails = { orgDetails: { @@ -227,39 +204,24 @@ export const updateOrganization = new mutationWithClientMutationId({ }, } - if (permission === 'super_admin' && typeof args.externallyManaged !== 'undefined') { + if (ac.can(permission).updateAny('organization').granted && typeof args.externallyManaged !== 'undefined') { updatedOrgDetails.externallyManaged = args.externallyManaged } - // Setup Trans action - const trx = await transaction(collections) + if (ac.can(permission).updateAny('organization').granted) { + updatedOrgDetails.externalId = externalId || compareOrg?.externalId - // Upsert new org details - try { - await trx.step( - async () => - await query` - WITH organizations - UPSERT { _key: ${orgKey} } - INSERT ${updatedOrgDetails} - UPDATE ${updatedOrgDetails} - IN organizations - `, - ) - } catch (err) { - console.error(`Transaction error occurred while upserting org: ${orgKey}, err: ${err}`) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) + const currentPolicies = compareOrg?.policies || {} + updatedOrgDetails.policies = { + psd: args.psd ?? currentPolicies.psd ?? false, + pgs: args.pgs ?? currentPolicies.pgs ?? false, + } } - try { - await trx.commit() - } catch (err) { - console.error(`Transaction error occurred while committing org: ${orgKey}, err: ${err}`) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } + await organizationDS.update({ orgKey, updatedOrgDetails }) - await loadOrgByKey.clear(orgKey) - const organization = await loadOrgByKey.load(orgKey) + await organizationDS.byKey.clear(orgKey) + const organization = await organizationDS.byKey.load(orgKey) console.info(`User: ${userKey}, successfully updated org ${orgKey}.`) @@ -293,14 +255,12 @@ export const updateOrganization = new mutationWithClientMutationId({ }) } if (updatedProperties.length > 0) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'update', target: { @@ -308,7 +268,7 @@ export const updateOrganization = new mutationWithClientMutationId({ en: compareOrg.orgDetails.en.name, fr: compareOrg.orgDetails.fr.name, }, - resourceType: 'organization', // user, org, domain + resourceType: 'organization', updatedProperties, }, }) diff --git a/api/src/organization/mutations/verify-organization.js b/api/src/organization/mutations/verify-organization.js index f682b6847..dbb2bb253 100644 --- a/api/src/organization/mutations/verify-organization.js +++ b/api/src/organization/mutations/verify-organization.js @@ -24,12 +24,9 @@ export const verifyOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, auth: { checkPermission, userRequired, verifiedRequired }, - loaders: { loadOrgByKey }, + dataSources: { organization: organizationDS }, validators: { cleanseInput }, }, ) => { @@ -41,7 +38,7 @@ export const verifyOrganization = new mutationWithClientMutationId({ const { id: orgKey } = fromGlobalId(cleanseInput(args.orgId)) // Check to see if org exists - const currentOrg = await loadOrgByKey.load(orgKey) + const currentOrg = await organizationDS.byKey.load(orgKey) if (typeof currentOrg === 'undefined') { console.warn( @@ -82,51 +79,9 @@ export const verifyOrganization = new mutationWithClientMutationId({ } } - // Set org to verified currentOrg.verified = true - // Setup Trans action - const trx = await transaction(collections) - - // Upsert new org details - try { - await trx.step( - () => - query` - WITH organizations - UPSERT { _key: ${orgKey} } - INSERT ${currentOrg} - UPDATE ${currentOrg} - IN organizations - `, - ) - } catch (err) { - console.error(`Transaction error occurred while upserting verified org: ${orgKey}, err: ${err}`) - throw new Error(i18n._(t`Unable to verify organization. Please try again.`)) - } - - // unarchive all archived affiliated domains - try { - await trx.step( - () => - query` - WITH domains, claims - FOR v, e IN 1..1 OUTBOUND ${currentOrg._id} claims - FILTER v.archived == true - UPDATE v WITH { archived: false } IN domains - `, - ) - } catch (err) { - console.error(`Transaction error occurred while unarchiving affiliated domains for org: ${orgKey}, err: ${err}`) - throw new Error(i18n._(t`Unable to verify organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction error occurred while committing newly verified org: ${orgKey}, err: ${err}`) - throw new Error(i18n._(t`Unable to verify organization. Please try again.`)) - } + await organizationDS.verify({ currentOrg }) console.info(`User: ${userKey}, successfully verified org: ${orgKey}.`) diff --git a/api/src/organization/objects/__tests__/organization-summary.test.js b/api/src/organization/objects/__tests__/organization-summary.test.js index 273661e99..ff202a755 100644 --- a/api/src/organization/objects/__tests__/organization-summary.test.js +++ b/api/src/organization/objects/__tests__/organization-summary.test.js @@ -326,7 +326,6 @@ describe('given the organization summary object', () => { const demoType = organizationSummaryType.getFields() const dmarcPhase = { - not_implemented: 0, assess: 0, deploy: 0, enforce: 0, @@ -337,7 +336,6 @@ describe('given the organization summary object', () => { const i18n = { _: jest .fn() - .mockReturnValueOnce('not_implemented') .mockReturnValueOnce('assess') .mockReturnValueOnce('deploy') .mockReturnValueOnce('enforce') @@ -346,11 +344,6 @@ describe('given the organization summary object', () => { expect(demoType.dmarcPhase.resolve({ dmarc_phase: dmarcPhase }, {}, { i18n })).toEqual({ categories: [ - { - count: 0, - name: 'not implemented', - percentage: 0, - }, { count: 0, name: 'assess', @@ -382,18 +375,16 @@ describe('given the organization summary object', () => { const demoType = organizationSummaryType.getFields() const dmarcPhase = { - not_implemented: 50, assess: 75, deploy: 100, enforce: 125, - maintain: 150, + maintain: 200, total: 500, } const i18n = { _: jest .fn() - .mockReturnValueOnce('not_implemented') .mockReturnValueOnce('assess') .mockReturnValueOnce('deploy') .mockReturnValueOnce('enforce') @@ -402,11 +393,6 @@ describe('given the organization summary object', () => { expect(demoType.dmarcPhase.resolve({ dmarc_phase: dmarcPhase }, {}, { i18n })).toEqual({ categories: [ - { - count: 50, - name: 'not implemented', - percentage: 10.0, - }, { count: 75, name: 'assess', @@ -423,9 +409,9 @@ describe('given the organization summary object', () => { percentage: 25.0, }, { - count: 150, + count: 200, name: 'maintain', - percentage: 30.0, + percentage: 40.0, }, ], total: 500, diff --git a/api/src/organization/objects/__tests__/organization.test.js b/api/src/organization/objects/__tests__/organization.test.js index 871611b23..275ec4cf1 100644 --- a/api/src/organization/objects/__tests__/organization.test.js +++ b/api/src/organization/objects/__tests__/organization.test.js @@ -213,7 +213,6 @@ describe('given the organization object', () => { describe('testing the domains resolver', () => { it('returns the resolved value', async () => { const demoType = organizationType.getFields() - const checkPermission = jest.fn().mockReturnValue('user') const expectedResult = { edges: [ @@ -243,9 +242,9 @@ describe('given the organization object', () => { { _id: 'organizations/1' }, { first: 1 }, { - auth: { checkPermission }, - loaders: { - loadDomainConnectionsByOrgId: jest.fn().mockReturnValue(expectedResult), + dataSources: { + auth: { permissionByOrgId: { load: jest.fn().mockResolvedValue('user') } }, + domain: { connectionsByOrgId: jest.fn().mockReturnValue(expectedResult) }, }, }, ), @@ -257,8 +256,6 @@ describe('given the organization object', () => { it('returns the resolved value', async () => { const demoType = organizationType.getFields() - const checkPermission = jest.fn().mockReturnValue('admin') - const expectedResults = { edges: [ { @@ -291,9 +288,11 @@ describe('given the organization object', () => { { _id: 'organizations/1' }, { first: 5 }, { - auth: { checkPermission }, - loaders: { - loadAffiliationConnectionsByOrgId: jest.fn().mockReturnValue(expectedResults), + i18n, + auth: { loginRequiredBool: true }, + dataSources: { + auth: { permissionByOrgId: { load: jest.fn().mockResolvedValue('admin') } }, + affiliation: { connectionsByOrgId: jest.fn().mockReturnValue(expectedResults) }, }, }, ), @@ -319,16 +318,17 @@ describe('given the organization object', () => { it('returns the resolved value', async () => { const demoType = organizationType.getFields() - const checkPermission = jest.fn().mockReturnValue('user') - try { await demoType.affiliations.resolve( { _id: '1' }, { first: 5 }, { i18n, - auth: { checkPermission }, - loaders: { loadAffiliationConnectionsByOrgId: jest.fn() }, + auth: { loginRequiredBool: true }, + dataSources: { + auth: { permissionByOrgId: { load: jest.fn().mockResolvedValue('user') } }, + affiliation: { connectionsByOrgId: jest.fn() }, + }, }, ) } catch (err) { @@ -356,16 +356,17 @@ describe('given the organization object', () => { it('returns the resolved value', async () => { const demoType = organizationType.getFields() - const checkPermission = jest.fn().mockReturnValue('user') - try { await demoType.affiliations.resolve( { _id: '1' }, { first: 5 }, { i18n, - auth: { checkPermission }, - loaders: { loadAffiliationConnectionsByOrgId: jest.fn() }, + auth: { loginRequiredBool: true }, + dataSources: { + auth: { permissionByOrgId: { load: jest.fn().mockResolvedValue('user') } }, + affiliation: { connectionsByOrgId: jest.fn() }, + }, }, ) } catch (err) { diff --git a/api/src/organization/objects/index.js b/api/src/organization/objects/index.js index c37ed856e..15cdf22f6 100644 --- a/api/src/organization/objects/index.js +++ b/api/src/organization/objects/index.js @@ -2,5 +2,5 @@ export * from './organization' export * from './organization-connection' export * from './organization-error' export * from './organization-result' +export * from './organization-policies' export * from './organization-summary' -export * from './organization-summary-connection' diff --git a/api/src/organization/objects/organization-policies.js b/api/src/organization/objects/organization-policies.js new file mode 100644 index 000000000..ac6001849 --- /dev/null +++ b/api/src/organization/objects/organization-policies.js @@ -0,0 +1,18 @@ +import { GraphQLBoolean, GraphQLObjectType } from 'graphql' + +export const organizationPoliciesType = new GraphQLObjectType({ + name: 'OrganizationPolicies', + description: 'Policies that apply to a given organization.', + fields: () => ({ + psd: { + type: GraphQLBoolean, + description: 'Whether the Policy on Service and Digital applies to this organization.', + resolve: ({ psd }) => psd ?? false, + }, + pgs: { + type: GraphQLBoolean, + description: 'Whether the Policy on Government Security applies to this organization.', + resolve: ({ pgs }) => pgs ?? false, + }, + }), +}) \ No newline at end of file diff --git a/api/src/organization/objects/organization-summary-connection.js b/api/src/organization/objects/organization-summary-connection.js deleted file mode 100644 index aaee4c34b..000000000 --- a/api/src/organization/objects/organization-summary-connection.js +++ /dev/null @@ -1,16 +0,0 @@ -import { GraphQLInt } from 'graphql' -import { connectionDefinitions } from 'graphql-relay' - -import { organizationSummaryType } from './organization-summary' - -export const orgSummaryConnection = connectionDefinitions({ - name: 'OrganizationSummary', - nodeType: organizationSummaryType, - connectionFields: () => ({ - totalCount: { - type: GraphQLInt, - description: 'The total amount of dmarc summaries the user has access to.', - resolve: ({ totalCount }) => totalCount, - }, - }), -}) diff --git a/api/src/organization/objects/organization-summary.js b/api/src/organization/objects/organization-summary.js index 9d48b6eb6..dbeaa8248 100644 --- a/api/src/organization/objects/organization-summary.js +++ b/api/src/organization/objects/organization-summary.js @@ -2,6 +2,23 @@ import { GraphQLObjectType } from 'graphql' import { categorizedSummaryType } from '../../summaries' import { GraphQLDate } from 'graphql-scalars' +import { guidanceTagOrder, guidanceTagConnection } from '../../guidance-tag' + +const calculatePercentage = (numerator, denominator) => { + if (denominator <= 0) { + return 0 + } else { + return Number(((numerator / denominator) * 100).toFixed(1)) + } +} + +const createCategory = (name, count, total) => { + return { + name, + count, + percentage: calculatePercentage(count, total), + } +} export const organizationSummaryType = new GraphQLObjectType({ name: 'OrganizationSummary', @@ -16,26 +33,9 @@ export const organizationSummaryType = new GraphQLObjectType({ type: categorizedSummaryType, description: 'Summary based on DMARC scan results for a given organization.', resolve: ({ dmarc }, _) => { - let percentPass, percentageFail - if (dmarc.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((dmarc.pass / dmarc.total) * 100).toFixed(1)) - percentageFail = Number(((dmarc.fail / dmarc.total) * 100).toFixed(1)) - } - const categories = [ - { - name: 'pass', - count: dmarc.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: dmarc.fail, - percentage: percentageFail, - }, + createCategory('pass', dmarc.pass, dmarc.total), + createCategory('fail', dmarc.fail, dmarc.total), ] return { @@ -48,26 +48,9 @@ export const organizationSummaryType = new GraphQLObjectType({ type: categorizedSummaryType, description: 'Summary based on HTTPS scan results for a given organization.', resolve: ({ https }, _) => { - let percentPass, percentageFail - if (https.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((https.pass / https.total) * 100).toFixed(1)) - percentageFail = Number(((https.fail / https.total) * 100).toFixed(1)) - } - const categories = [ - { - name: 'pass', - count: https.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: https.fail, - percentage: percentageFail, - }, + createCategory('pass', https.pass, https.total), + createCategory('fail', https.fail, https.total), ] return { @@ -80,26 +63,9 @@ export const organizationSummaryType = new GraphQLObjectType({ type: categorizedSummaryType, description: 'Summary based on mail scan results for a given organization.', resolve: ({ mail }, _) => { - let percentPass, percentageFail - if (mail.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((mail.pass / mail.total) * 100).toFixed(1)) - percentageFail = Number(((mail.fail / mail.total) * 100).toFixed(1)) - } - const categories = [ - { - name: 'pass', - count: mail.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: mail.fail, - percentage: percentageFail, - }, + createCategory('pass', mail.pass, mail.total), + createCategory('fail', mail.fail, mail.total), ] return { @@ -112,27 +78,7 @@ export const organizationSummaryType = new GraphQLObjectType({ type: categorizedSummaryType, description: 'Summary based on web scan results for a given organization.', resolve: ({ web }, _) => { - let percentPass, percentageFail - if (web.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((web.pass / web.total) * 100).toFixed(1)) - percentageFail = Number(((web.fail / web.total) * 100).toFixed(1)) - } - - const categories = [ - { - name: 'pass', - count: web.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: web.fail, - percentage: percentageFail, - }, - ] + const categories = [createCategory('pass', web.pass, web.total), createCategory('fail', web.fail, web.total)] return { categories, @@ -143,53 +89,17 @@ export const organizationSummaryType = new GraphQLObjectType({ dmarcPhase: { type: categorizedSummaryType, description: 'Summary based on DMARC phases for a given organization.', - resolve: ({ dmarc_phase }, _) => { - let percentNotImplemented, percentAssess, percentDeploy, percentEnforce, percentMaintain - if (dmarc_phase.total <= 0) { - percentNotImplemented = 0 - percentAssess = 0 - percentDeploy = 0 - percentEnforce = 0 - percentMaintain = 0 - } else { - percentNotImplemented = Number(((dmarc_phase.not_implemented / dmarc_phase.total) * 100).toFixed(1)) - percentAssess = Number(((dmarc_phase.assess / dmarc_phase.total) * 100).toFixed(1)) - percentDeploy = Number(((dmarc_phase.deploy / dmarc_phase.total) * 100).toFixed(1)) - percentEnforce = Number(((dmarc_phase.enforce / dmarc_phase.total) * 100).toFixed(1)) - percentMaintain = Number(((dmarc_phase.maintain / dmarc_phase.total) * 100).toFixed(1)) - } - + resolve: ({ dmarc_phase: dmarcPhase }, _) => { const categories = [ - { - name: 'not implemented', - count: dmarc_phase.not_implemented, - percentage: percentNotImplemented, - }, - { - name: 'assess', - count: dmarc_phase.assess, - percentage: percentAssess, - }, - { - name: 'deploy', - count: dmarc_phase.deploy, - percentage: percentDeploy, - }, - { - name: 'enforce', - count: dmarc_phase.enforce, - percentage: percentEnforce, - }, - { - name: 'maintain', - count: dmarc_phase.maintain, - percentage: percentMaintain, - }, + createCategory('assess', dmarcPhase.assess, dmarcPhase.total), + createCategory('deploy', dmarcPhase.deploy, dmarcPhase.total), + createCategory('enforce', dmarcPhase.enforce, dmarcPhase.total), + createCategory('maintain', dmarcPhase.maintain, dmarcPhase.total), ] return { categories, - total: dmarc_phase.total, + total: dmarcPhase.total, } }, }, @@ -197,27 +107,7 @@ export const organizationSummaryType = new GraphQLObjectType({ type: categorizedSummaryType, description: 'Summary based on SSL scan results for a given organization.', resolve: ({ ssl }, _) => { - let percentPass, percentageFail - if (ssl.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((ssl.pass / ssl.total) * 100).toFixed(1)) - percentageFail = Number(((ssl.fail / ssl.total) * 100).toFixed(1)) - } - - const categories = [ - { - name: 'pass', - count: ssl.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: ssl.fail, - percentage: percentageFail, - }, - ] + const categories = [createCategory('pass', ssl.pass, ssl.total), createCategory('fail', ssl.fail, ssl.total)] return { categories, @@ -228,32 +118,15 @@ export const organizationSummaryType = new GraphQLObjectType({ webConnections: { type: categorizedSummaryType, description: 'Summary based on HTTPS and HSTS scan results for a given organization.', - resolve: ({ web_connections }, _) => { - let percentPass, percentageFail - if (web_connections.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((web_connections.pass / web_connections.total) * 100).toFixed(1)) - percentageFail = Number(((web_connections.fail / web_connections.total) * 100).toFixed(1)) - } - + resolve: ({ web_connections: webConnections }, _) => { const categories = [ - { - name: 'pass', - count: web_connections.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: web_connections.fail, - percentage: percentageFail, - }, + createCategory('pass', webConnections.pass, webConnections.total), + createCategory('fail', webConnections.fail, webConnections.total), ] return { categories, - total: web_connections.total, + total: webConnections.total, } }, }, @@ -261,27 +134,7 @@ export const organizationSummaryType = new GraphQLObjectType({ type: categorizedSummaryType, description: 'Summary based on SPF scan results for a given organization.', resolve: ({ spf }, _) => { - let percentPass, percentageFail - if (spf.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((spf.pass / spf.total) * 100).toFixed(1)) - percentageFail = Number(((spf.fail / spf.total) * 100).toFixed(1)) - } - - const categories = [ - { - name: 'pass', - count: spf.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: spf.fail, - percentage: percentageFail, - }, - ] + const categories = [createCategory('pass', spf.pass, spf.total), createCategory('fail', spf.fail, spf.total)] return { categories, @@ -293,26 +146,9 @@ export const organizationSummaryType = new GraphQLObjectType({ type: categorizedSummaryType, description: 'Summary based on DKIM scan results for a given organization.', resolve: ({ dkim }, _) => { - let percentPass, percentageFail - if (dkim.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((dkim.pass / dkim.total) * 100).toFixed(1)) - percentageFail = Number(((dkim.fail / dkim.total) * 100).toFixed(1)) - } - const categories = [ - { - name: 'pass', - count: dkim.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: dkim.fail, - percentage: percentageFail, - }, + createCategory('pass', dkim.pass, dkim.total), + createCategory('fail', dkim.fail, dkim.total), ] return { @@ -321,5 +157,31 @@ export const organizationSummaryType = new GraphQLObjectType({ } }, }, + negativeFindings: { + type: guidanceTagConnection, + description: 'Aggregated negative findings for a given organization.', + args: { + orderBy: { + type: guidanceTagOrder, + description: 'Ordering options for guidance tag connections.', + }, + }, + resolve: async ( + { negative_tags: negativeTags }, + args, + { auth: { loginRequiredBool, userRequired, verifiedRequired }, dataSources: { guidanceTag } }, + ) => { + if (loginRequiredBool) { + const user = await userRequired() + verifiedRequired({ user }) + } + + const guidanceTags = await guidanceTag.summaryConnectionsByTagId({ + guidanceTags: negativeTags, + ...args, + }) + return guidanceTags + }, + }, }), }) diff --git a/api/src/organization/objects/organization.js b/api/src/organization/objects/organization.js index 29160edb3..cfbe7a3a1 100644 --- a/api/src/organization/objects/organization.js +++ b/api/src/organization/objects/organization.js @@ -2,16 +2,17 @@ import { t } from '@lingui/macro' import { GraphQLBoolean, GraphQLInt, GraphQLObjectType, GraphQLString, GraphQLList, GraphQLNonNull } from 'graphql' import { connectionArgs, globalIdField } from 'graphql-relay' +import { organizationPoliciesType } from './organization-policies' import { organizationSummaryType } from './organization-summary' import { nodeInterface } from '../../node' -import { Acronym, Slug, Year } from '../../scalars' +import { Acronym, Slug } from '../../scalars' import { affiliationUserOrder } from '../../affiliation/inputs' import { affiliationConnection } from '../../affiliation/objects' import { domainOrder, domainFilter } from '../../domain/inputs' import { domainConnection } from '../../domain/objects' -import { logActivity } from '../../audit-logs' -import { PeriodEnums } from '../../enums' -import { orgSummaryConnection } from './organization-summary-connection' +import { OrderDirection } from '../../enums' +import { tagType } from '../../tags/objects' +import ac from '../../access-control' export const organizationType = new GraphQLObjectType({ name: 'Organization', @@ -67,22 +68,77 @@ export const organizationType = new GraphQLObjectType({ description: 'Whether the organization is externally managed.', resolve: ({ externallyManaged }) => externallyManaged, }, + externalId: { + type: GraphQLString, + description: 'String ID used to identify the organization in an external system.', + resolve: ({ externalId }) => externalId, + }, + availableTags: { + type: new GraphQLList(tagType), + description: '', + args: { + includeGlobal: { + type: GraphQLBoolean, + description: '', + }, + includePending: { + type: GraphQLBoolean, + description: '', + }, + sortDirection: { + type: new GraphQLNonNull(OrderDirection), + description: 'The direction in which to sort the data.', + }, + }, + resolve: async ( + { _key }, + args, + { userKey, auth: { userRequired, loginRequiredBool, verifiedRequired }, dataSources: { tags } }, + ) => { + if (loginRequiredBool) { + const user = await userRequired() + verifiedRequired({ user }) + } + + const orgTags = await tags.byOrg({ + orgId: _key, + ...args, + }) + + console.debug(`User: ${userKey} successfully retrieved their org's tags.`) + + return orgTags + }, + }, + policies: { + type: organizationPoliciesType, + description: 'Policies that apply to this organization.', + resolve: ({ policies }) => policies, + }, summaries: { type: organizationSummaryType, description: 'Summaries based on scan types that are preformed on the given organizations domains.', resolve: ({ summaries }) => summaries, }, historicalSummaries: { - type: orgSummaryConnection.connectionType, - description: 'Historical summaries based on scan types that are preformed on the given organizations domains.', + type: new GraphQLList(organizationSummaryType), + description: 'Historical summaries based on scan types that are performed on the given organizations domains.', args: { - month: { - type: new GraphQLNonNull(PeriodEnums), - description: 'The month in which the returned data is relevant to.', + startDate: { + type: GraphQLString, + description: 'The start date for the returned data (YYYY-MM-DD).', }, - year: { - type: new GraphQLNonNull(Year), - description: 'The year in which the returned data is relevant to.', + endDate: { + type: GraphQLString, + description: 'The end date for the returned data (YYYY-MM-DD).', + }, + sortDirection: { + type: OrderDirection, + description: 'The direction in which to sort the data.', + }, + limit: { + type: GraphQLInt, + description: 'The maximum amount of summaries to be returned.', }, }, resolve: async ( @@ -91,7 +147,7 @@ export const organizationType = new GraphQLObjectType({ { userKey, auth: { userRequired, loginRequiredBool, verifiedRequired }, - loaders: { loadOrganizationSummariesByPeriod }, + dataSources: { organization: organizationDS }, }, ) => { if (loginRequiredBool) { @@ -99,14 +155,12 @@ export const organizationType = new GraphQLObjectType({ verifiedRequired({ user }) } - const historicalSummaries = await loadOrganizationSummariesByPeriod({ + const historicalSummaries = await organizationDS.summariesByPeriod({ orgId: _id, - period: args.month, ...args, }) - console.info(`User: ${userKey} successfully retrieved their chart summaries.`) - + console.info(`User: ${userKey} successfully retrieved their organization summaries.`) return historicalSummaries }, }, @@ -131,30 +185,29 @@ export const organizationType = new GraphQLObjectType({ { i18n, userKey, - query, - transaction, - collections, - auth: { checkPermission, userRequired, verifiedRequired }, - loaders: { loadOrganizationDomainStatuses }, + request: { ip }, + auth: { userRequired, verifiedRequired }, + dataSources: { auth: authDS, auditLogs, organization: organizationDS }, }, ) => { const user = await userRequired() verifiedRequired({ user }) - const permission = await checkPermission({ orgId: _id }) - if (!['user', 'admin', 'owner', 'super_admin'].includes(permission)) { + const permission = await authDS.permissionByOrgId.load(_id) + if (!ac.can(permission).createOwn('csv').granted) { console.error( `User "${userKey}" attempted to retrieve CSV output for organization "${_id}". Permission: ${permission}`, ) throw new Error(t`Permission Denied: Please contact organization user for help with retrieving this domain.`) } - const domains = await loadOrganizationDomainStatuses({ + const domains = await organizationDS.domainStatuses({ orgId: _id, ...args, }) const headers = [ 'domain', + 'ipAddresses', 'https', 'hsts', 'certificates', @@ -164,59 +217,66 @@ export const organizationType = new GraphQLObjectType({ 'spf', 'dkim', 'dmarc', + 'phase', 'tags', - 'hidden', + 'assetState', 'rcode', 'blocked', 'wildcardSibling', + 'wildcardEntry', + 'hasEntrustCertificate', + 'top25Vulnerabilities', + 'cvdEnrollmentStatus', ] let csvOutput = headers.join(',') - domains.forEach((domain) => { - let csvLine = `${domain.domain}` - csvLine += headers.slice(1, 10).reduce((previousValue, currentHeader) => { - return `${previousValue},${domain.status[currentHeader]}` - }, '') - csvLine += `,${domain.tags.join('|')},${domain.hidden},${domain.rcode},${domain.blocked},${ - domain.wildcardSibling - }` + domains.forEach((domainDoc) => { + const csvLine = headers + .map((header) => { + if (['ipAddresses', 'tags', 'top25Vulnerabilities'].includes(header)) { + return `"${domainDoc[header]?.join('|') || []}"` + } else if ( + ['https', 'hsts', 'certificates', 'protocols', 'ciphers', 'curves', 'spf', 'dkim', 'dmarc'].includes( + header, + ) + ) { + return `"${domainDoc?.status[header]}"` + } else if (header === 'phase') { + switch (domainDoc[header]) { + case 'assess': + return i18n._(t`Assess`) + case 'deploy': + return i18n._(t`Deploy`) + case 'enforce': + return i18n._(t`Enforce`) + case 'maintain': + return i18n._(t`Maintain`) + default: + return '' + } + } + return `"${domainDoc[header]}"` + }) + .join(',') csvOutput += `\n${csvLine}` }) // Get org names to use in activity log - let orgNamesCursor - try { - orgNamesCursor = await query` - LET org = DOCUMENT(organizations, ${_id}) - RETURN { - "orgNameEN": org.orgDetails.en.name, - "orgNameFR": org.orgDetails.fr.name, - } - ` - } catch (err) { - console.error( - `Database error occurred when user: ${userKey} attempted to export org: ${_id}. Error while creating cursor for retrieving organization names. error: ${err}`, - ) - throw new Error(i18n._(t`Unable to export organization. Please try again.`)) - } - let orgNames try { - orgNames = await orgNamesCursor.next() + orgNames = await organizationDS.namesById.load(_id) } catch (err) { console.error( - `Cursor error occurred when user: ${userKey} attempted to export org: ${_id}. Error while retrieving organization names. error: ${err}`, + `Error occurred when user: ${userKey} attempted to export org: ${_id}. Error while retrieving organization names. error: ${err}`, ) throw new Error(i18n._(t`Unable to export organization. Please try again.`)) } - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, role: permission, + ipAddress: ip, }, action: 'export', target: { @@ -227,7 +287,7 @@ export const organizationType = new GraphQLObjectType({ organization: { id: _id, name: orgNames.orgNameEN, - }, // name of resource being acted upon + }, resourceType: 'organization', }, }) @@ -261,11 +321,10 @@ export const organizationType = new GraphQLObjectType({ { _id }, args, - { auth: { checkPermission }, loaders: { loadDomainConnectionsByOrgId } }, + { dataSources: { auth: authDS, domain: domainDataSource } }, ) => { - // Check to see requesting users permission to the org is - const permission = await checkPermission({ orgId: _id }) - const connections = await loadDomainConnectionsByOrgId({ + const permission = await authDS.permissionByOrgId.load(_id) + const connections = await domainDataSource.connectionsByOrgId({ orgId: _id, permission, ...args, @@ -294,14 +353,18 @@ export const organizationType = new GraphQLObjectType({ resolve: async ( { _id }, args, - { i18n, auth: { checkPermission }, loaders: { loadAffiliationConnectionsByOrgId } }, + { + i18n, + auth: { loginRequiredBool }, + dataSources: { auth: authDS, affiliation }, + }, ) => { - const permission = await checkPermission({ orgId: _id }) - if (['admin', 'owner', 'super_admin'].includes(permission) === false) { + const permission = await authDS.permissionByOrgId.load(_id) + if (!ac.can(permission).readOwn('affiliation').granted && loginRequiredBool) { throw new Error(i18n._(t`Cannot query affiliations on organization without admin permission or higher.`)) } - const affiliations = await loadAffiliationConnectionsByOrgId({ + const affiliations = await affiliation.connectionsByOrgId({ orgId: _id, ...args, }) @@ -312,8 +375,8 @@ export const organizationType = new GraphQLObjectType({ type: GraphQLBoolean, description: 'Value that determines if a user is affiliated with an organization, whether through organization affiliation, verified affiliation, or through super admin status.', - resolve: async ({ _id }, _args, { auth: { checkPermission } }) => { - const permission = await checkPermission({ orgId: _id }) + resolve: async ({ _id }, _args, { dataSources: { auth: authDS } }) => { + const permission = await authDS.permissionByOrgId.load(_id) return ['user', 'admin', 'super_admin', 'owner'].includes(permission) }, }, diff --git a/api/src/organization/queries/__tests__/find-my-organizations.test.js b/api/src/organization/queries/__tests__/find-my-organizations.test.js index 397879759..11ab3ea76 100644 --- a/api/src/organization/queries/__tests__/find-my-organizations.test.js +++ b/api/src/organization/queries/__tests__/find-my-organizations.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -54,7 +55,6 @@ describe('given findMyOrganizationsQuery', () => { user = await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'french', emailValidated: true, }) @@ -194,15 +194,13 @@ describe('given findMyOrganizationsQuery', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', - }), - }, + }) } }, }, }) @@ -337,15 +335,13 @@ describe('given findMyOrganizationsQuery', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'fr', - }), - }, + }) } }, }, }) @@ -457,16 +453,14 @@ describe('given findMyOrganizationsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query: mockedQuery, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', i18n, - }), - }, + }) } }, }, }) @@ -535,16 +529,14 @@ describe('given findMyOrganizationsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query: mockedQuery, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', i18n, - }), - }, + }) } }, }, }) diff --git a/api/src/organization/queries/__tests__/find-organization-by-slug.test.js b/api/src/organization/queries/__tests__/find-organization-by-slug.test.js index d91d62cb2..95e441eec 100644 --- a/api/src/organization/queries/__tests__/find-organization-by-slug.test.js +++ b/api/src/organization/queries/__tests__/find-organization-by-slug.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -9,7 +10,6 @@ import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired } from '../../../auth' -import { loadAffiliationConnectionsByOrgId } from '../../../affiliation/loaders' import { loadDomainConnectionsByOrgId } from '../../../domain/loaders' import { loadUserByKey } from '../../../user/loaders' import { loadOrgBySlug, loadOrgByKey } from '../../loaders' @@ -158,9 +158,13 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, + dataSources: { + organization: { + byKey: loadOrgByKey(query, 'en'), + bySlug: loadOrgBySlug({ query, language: 'en' }), + }, + }, loaders: { - loadOrgByKey: loadOrgByKey(query, 'en'), - loadOrgBySlug: loadOrgBySlug({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), loadDomainConnectionsByOrgId: loadDomainConnectionsByOrgId({ query, @@ -169,13 +173,6 @@ describe('given findOrganizationBySlugQuery', () => { auth: { loginRequiredBool: true }, i18n, }), - loadAffiliationConnectionsByOrgId: loadAffiliationConnectionsByOrgId({ - query, - userKey: user._key, - cleanseInput, - auth: { loginRequiredBool: true }, - i18n, - }), }, }, }) @@ -264,9 +261,13 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, + dataSources: { + organization: { + byKey: loadOrgByKey(query, 'fr'), + bySlug: loadOrgBySlug({ query, language: 'fr' }), + }, + }, loaders: { - loadOrgByKey: loadOrgByKey(query, 'fr'), - loadOrgBySlug: loadOrgBySlug({ query, language: 'fr' }), loadUserByKey: loadUserByKey({ query }), loadDomainConnectionsByOrgId: loadDomainConnectionsByOrgId({ query, @@ -275,12 +276,6 @@ describe('given findOrganizationBySlugQuery', () => { auth: { loginRequiredBool: true }, i18n, }), - loadAffiliationConnectionsByOrgId: loadAffiliationConnectionsByOrgId({ - query, - userKey: user._key, - cleanseInput, - i18n, - }), }, }, }) @@ -360,11 +355,9 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue(), - }, - }, + } } }, }, }) @@ -409,11 +402,9 @@ describe('given findOrganizationBySlugQuery', () => { validators: { cleanseInput, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue({}), - }, - }, + } } }, }, }) @@ -476,11 +467,9 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue(), - }, - }, + } } }, }, }) @@ -525,11 +514,9 @@ describe('given findOrganizationBySlugQuery', () => { validators: { cleanseInput, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue({}), - }, - }, + } } }, }, }) diff --git a/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js b/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js index 48c2f8c62..b8a25f832 100644 --- a/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js +++ b/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js @@ -1,9 +1,10 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' -import { checkSuperAdmin, userRequired, verifiedRequired } from '../../../auth' +import { checkSuperAdmin, superAdminRequired, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' import { loadAllOrganizationDomainStatuses } from '../../loaders' import dbschema from '../../../../database.json' @@ -14,7 +15,8 @@ import frenchMessages from '../../../locale/fr/messages' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given getAllOrganizationDomainStatuses', () => { - let query, drop, truncate, schema, collections, orgOne, orgTwo, superAdminOrg, domainOne, domainTwo, i18n, user + // eslint-disable-next-line no-unused-vars + let query, drop, truncate, schema, collections, superAdminOrg, domainOne, domainTwo, i18n, user, orgOne const consoleOutput = [] const mockedInfo = (output) => consoleOutput.push(output) @@ -65,7 +67,6 @@ describe('given getAllOrganizationDomainStatuses', () => { user = await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: true, }) superAdminOrg = await collections.organizations.save({ @@ -92,59 +93,14 @@ describe('given getAllOrganizationDomainStatuses', () => { }, }, }) - orgOne = await collections.organizations.save({ - orgDetails: { - en: { - slug: 'definitely-treasury-board-secretariat', - acronym: 'NTBS', - name: 'Definitely Treasury Board of Canada Secretariat', - zone: 'NFED', - sector: 'NTBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'definitivement-secretariat-conseil-tresor', - acronym: 'NPSCT', - name: 'Définitivement Secrétariat du Conseil du Trésor du Canada', - zone: 'NPFED', - sector: 'NPTBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }) - orgTwo = await collections.organizations.save({ - orgDetails: { - en: { - slug: 'not-treasury-board-secretariat', - acronym: 'NTBS', - name: 'Not Treasury Board of Canada Secretariat', - zone: 'NFED', - sector: 'NTBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'ne-pas-secretariat-conseil-tresor', - acronym: 'NPSCT', - name: 'Ne Pas Secrétariat du Conseil Trésor du Canada', - zone: 'NPFED', - sector: 'NPTBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }) + domainOne = await collections.domains.save({ domain: 'domain.one', + phase: 'assess', status: { https: 'fail', hsts: 'pass', + certificates: 'pass', ciphers: 'pass', curves: 'pass', protocols: 'pass', @@ -152,12 +108,20 @@ describe('given getAllOrganizationDomainStatuses', () => { dkim: 'pass', dmarc: 'pass', }, + rcode: 'NOERROR', + blocked: false, + wildcardSibling: false, + wildcardEntry: false, + hasEntrustCertificate: false, + cveDetected: false, }) domainTwo = await collections.domains.save({ domain: 'domain.two', + phase: 'deploy', status: { https: 'pass', hsts: 'fail', + certificates: 'pass', ciphers: 'fail', curves: 'pass', protocols: 'fail', @@ -165,14 +129,32 @@ describe('given getAllOrganizationDomainStatuses', () => { dkim: 'pass', dmarc: 'fail', }, + rcode: 'NOERROR', + blocked: false, + wildcardSibling: false, + wildcardEntry: false, + hasEntrustCertificate: false, + cveDetected: false, }) + + orgOne = await collections.organizations.save({ + orgDetails: { + en: { + name: 'Org One', + acronym: 'OO', + externalId: 'ORG123', + }, + }, + verified: true, + }) + await collections.claims.save({ - _from: orgOne._id, _to: domainOne._id, + _from: orgOne._id, }) await collections.claims.save({ - _from: orgTwo._id, _to: domainTwo._id, + _from: orgOne._id, }) }) afterEach(async () => { @@ -192,7 +174,7 @@ describe('given getAllOrganizationDomainStatuses', () => { schema, source: ` query { - getAllOrganizationDomainStatuses + getAllOrganizationDomainStatuses(filters: []) } `, rootValue: null, @@ -215,15 +197,14 @@ describe('given getAllOrganizationDomainStatuses', () => { }), }), verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, - }), - }, + }) } }, }, }) const error = [ @@ -231,7 +212,7 @@ describe('given getAllOrganizationDomainStatuses', () => { ] expect(response.errors).toEqual(error) expect(consoleOutput).toEqual([ - `User: ${user._key} attempted to load all organization statuses but login is required and they are not a super admin.`, + `User: ${user._key} attempted to access controlled functionality without sufficient privileges.`, ]) }) }) @@ -249,7 +230,7 @@ describe('given getAllOrganizationDomainStatuses', () => { schema, source: ` query { - getAllOrganizationDomainStatuses + getAllOrganizationDomainStatuses(filters: []) } `, rootValue: null, @@ -272,23 +253,23 @@ describe('given getAllOrganizationDomainStatuses', () => { }), }), verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, - }), - }, + language: 'en', + }) } }, }, }) const expectedResponse = { data: { - getAllOrganizationDomainStatuses: `Organization name (English),Nom de l'organisation (Français),Domain,HTTPS,HSTS,Ciphers,Curves,Protocols,SPF,DKIM,DMARC -"Definitely Treasury Board of Canada Secretariat","Définitivement Secrétariat du Conseil du Trésor du Canada","domain.one","fail","pass","pass","pass","pass","pass","pass","pass" -"Not Treasury Board of Canada Secretariat","Ne Pas Secrétariat du Conseil Trésor du Canada","domain.two","pass","fail","fail","pass","fail","pass","pass","fail"`, + getAllOrganizationDomainStatuses: `domain,orgNames,orgAcronyms,orgExternalIDs,ipAddresses,https,hsts,certificates,ciphers,curves,protocols,spf,dkim,dmarc,phase,rcode,blocked,wildcardSibling,wildcardEntry,hasEntrustCertificate,top25Vulnerabilities +"domain.one","Org One","OO","ORG123","","fail","pass","pass","pass","pass","pass","pass","pass","pass",Assess,"NOERROR","false","false","false","false","" +"domain.two","Org One","OO","ORG123","","pass","fail","pass","fail","pass","fail","pass","pass","fail",Deploy,"NOERROR","false","false","false","false",""`, }, } @@ -307,7 +288,7 @@ describe('given getAllOrganizationDomainStatuses', () => { schema, source: ` query { - getAllOrganizationDomainStatuses + getAllOrganizationDomainStatuses(filters: []) } `, rootValue: null, @@ -330,15 +311,14 @@ describe('given getAllOrganizationDomainStatuses', () => { }), }), verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, - }), - }, + }) } }, }, }) const error = [ @@ -347,7 +327,7 @@ describe('given getAllOrganizationDomainStatuses', () => { expect(response.errors).toEqual(error) expect(consoleOutput).toEqual([ - `User: ${user._key} attempted to load all organization statuses but login is required and they are not a super admin.`, + `User: ${user._key} attempted to access controlled functionality without sufficient privileges.`, ]) }) }) @@ -365,7 +345,7 @@ describe('given getAllOrganizationDomainStatuses', () => { schema, source: ` query { - getAllOrganizationDomainStatuses + getAllOrganizationDomainStatuses(filters: []) } `, rootValue: null, @@ -388,22 +368,22 @@ describe('given getAllOrganizationDomainStatuses', () => { }), }), verifiedRequired: verifiedRequired({}), + superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, - }), - }, + language: 'en', + }) } }, }, }) const expectedResponse = { data: { - getAllOrganizationDomainStatuses: `Organization name (English),Nom de l'organisation (Français),Domain,HTTPS,HSTS,Ciphers,Curves,Protocols,SPF,DKIM,DMARC -"Definitely Treasury Board of Canada Secretariat","Définitivement Secrétariat du Conseil du Trésor du Canada","domain.one","fail","pass","pass","pass","pass","pass","pass","pass" -"Not Treasury Board of Canada Secretariat","Ne Pas Secrétariat du Conseil Trésor du Canada","domain.two","pass","fail","fail","pass","fail","pass","pass","fail"`, + getAllOrganizationDomainStatuses: `domain,orgNames,orgAcronyms,orgExternalIDs,ipAddresses,https,hsts,certificates,ciphers,curves,protocols,spf,dkim,dmarc,phase,rcode,blocked,wildcardSibling,wildcardEntry,hasEntrustCertificate,top25Vulnerabilities +"domain.one","Org One","OO","ORG123","","fail","pass","pass","pass","pass","pass","pass","pass","pass",Assess,"NOERROR","false","false","false","false","" +"domain.two","Org One","OO","ORG123","","pass","fail","pass","fail","pass","fail","pass","pass","fail",Deploy,"NOERROR","false","false","false","false",""`, }, } expect(response).toEqual(expectedResponse) diff --git a/api/src/organization/queries/find-my-organizations.js b/api/src/organization/queries/find-my-organizations.js index 2c4752208..183872f8e 100644 --- a/api/src/organization/queries/find-my-organizations.js +++ b/api/src/organization/queries/find-my-organizations.js @@ -28,6 +28,18 @@ export const findMyOrganizations = { type: GraphQLBoolean, description: 'Filter org list to include only verified organizations.', }, + isAffiliated: { + type: GraphQLBoolean, + description: 'Filter the results based on the users affiliation.', + }, + hasPsd: { + type: GraphQLBoolean, + description: 'Filter org list to organizations the Policy on Service and Digital applies to.', + }, + hasPgs: { + type: GraphQLBoolean, + description: 'Filter org list to organizations the Policy on Government Security applies to.', + }, ...connectionArgs, }, resolve: async ( @@ -36,7 +48,7 @@ export const findMyOrganizations = { { userKey, auth: { checkSuperAdmin, userRequired, verifiedRequired, loginRequiredBool }, - loaders: { loadOrgConnectionsByUserId }, + dataSources: { organization: organizationDS }, }, ) => { if (loginRequiredBool) { @@ -46,7 +58,7 @@ export const findMyOrganizations = { const isSuperAdmin = await checkSuperAdmin() - const orgConnections = await loadOrgConnectionsByUserId({ + const orgConnections = await organizationDS.connectionsByUserId({ isSuperAdmin, ...args, }) diff --git a/api/src/organization/queries/find-organization-by-slug.js b/api/src/organization/queries/find-organization-by-slug.js index 9f7c4ed6d..90919afe5 100644 --- a/api/src/organization/queries/find-organization-by-slug.js +++ b/api/src/organization/queries/find-organization-by-slug.js @@ -1,6 +1,7 @@ import { GraphQLNonNull } from 'graphql' import { t } from '@lingui/macro' import { Slug } from '../../scalars' +import ac from '../../access-control' const { organizationType } = require('../objects') @@ -20,7 +21,7 @@ export const findOrganizationBySlug = { i18n, userKey, auth: { checkPermission, userRequired, verifiedRequired, loginRequiredBool }, - loaders: { loadOrgBySlug }, + dataSources: { organization: organizationDS }, validators: { cleanseInput }, }, ) => { @@ -34,7 +35,7 @@ export const findOrganizationBySlug = { const orgSlug = cleanseInput(args.orgSlug) // Retrieve organization by slug - const org = await loadOrgBySlug.load(orgSlug) + const org = await organizationDS.bySlug.load(orgSlug) if (typeof org === 'undefined') { console.warn(`User ${userKey} could not retrieve organization.`) @@ -44,16 +45,11 @@ export const findOrganizationBySlug = { // Check user permission for organization access const permission = await checkPermission({ orgId: org._id }) - if (loginRequiredBool) { - if (!['user', 'admin', 'owner', 'super_admin'].includes(permission)) { - console.warn(`User ${userKey} could not retrieve organization.`) - throw new Error(i18n._(t`Permission Denied: Could not retrieve specified organization.`)) - } - } else { - if (org.verified !== true && !['user', 'admin', 'owner', 'super_admin'].includes(permission)) { - console.warn(`User ${userKey} could not retrieve organization.`) - throw new Error(i18n._(t`Permission Denied: Could not retrieve specified organization.`)) - } + if (loginRequiredBool && !ac.can(permission).readOwn('organization').granted) { + console.warn(`User ${userKey} could not retrieve organization.`) + throw new Error(i18n._(t`Permission Denied: Could not retrieve specified organization.`)) + } else if (!loginRequiredBool && org.verified !== true && !ac.can(permission).readOwn('organization').granted) { + throw new Error(i18n._(t`Permission Denied: Could not retrieve specified organization.`)) } console.info(`User ${userKey} successfully retrieved organization ${org._key}.`) diff --git a/api/src/organization/queries/get-all-organization-domain-statuses.js b/api/src/organization/queries/get-all-organization-domain-statuses.js index 3956e7830..f982c98a2 100644 --- a/api/src/organization/queries/get-all-organization-domain-statuses.js +++ b/api/src/organization/queries/get-all-organization-domain-statuses.js @@ -1,14 +1,15 @@ -import { GraphQLBoolean, GraphQLString } from 'graphql' - +import { GraphQLString, GraphQLList } from 'graphql' +import { domainFilter } from '../../domain/inputs' +import { i18n } from '@lingui/core' import { t } from '@lingui/macro' export const getAllOrganizationDomainStatuses = { type: GraphQLString, description: 'CSV formatted output of all domains in all organizations including their email and web scan statuses.', args: { - blocked: { - type: GraphQLBoolean, - description: 'Whether to include blocked domains in the output.', + filters: { + type: new GraphQLList(domainFilter), + description: 'Filters used to limit domains returned.', }, }, resolve: async ( @@ -16,45 +17,69 @@ export const getAllOrganizationDomainStatuses = { args, { userKey, - i18n, - auth: { checkSuperAdmin, userRequired, verifiedRequired }, - loaders: { loadAllOrganizationDomainStatuses }, + auth: { checkSuperAdmin, userRequired, verifiedRequired, superAdminRequired }, + dataSources: { organization: organizationDS }, }, ) => { const user = await userRequired() verifiedRequired({ user }) const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ user, isSuperAdmin }) - if (!isSuperAdmin) { - console.warn( - `User: ${userKey} attempted to load all organization statuses but login is required and they are not a super admin.`, - ) - throw new Error(i18n._(t`Permissions error. You do not have sufficient permissions to access this data.`)) - } - - const domainStatuses = await loadAllOrganizationDomainStatuses({ ...args }) + const domainStatuses = await organizationDS.allDomainStatuses({ ...args }) console.info(`User ${userKey} successfully retrieved all domain statuses.`) if (domainStatuses === undefined) return domainStatuses const headers = [ - 'Organization name (English)', - "Nom de l'organisation (Français)", - 'Domain', - 'HTTPS', - 'HSTS', - 'Ciphers', - 'Curves', - 'Protocols', - 'SPF', - 'DKIM', - 'DMARC', + 'domain', + 'orgNames', + 'orgAcronyms', + 'orgExternalIDs', + 'ipAddresses', + 'https', + 'hsts', + 'certificates', + 'ciphers', + 'curves', + 'protocols', + 'spf', + 'dkim', + 'dmarc', + 'phase', + 'rcode', + 'blocked', + 'wildcardSibling', + 'wildcardEntry', + 'hasEntrustCertificate', + 'top25Vulnerabilities', ] let csvOutput = headers.join(',') domainStatuses.forEach((domainStatus) => { - const csvLine = headers.map((header) => `"${domainStatus[header]}"`).join(',') + const csvLine = headers + .map((header) => { + if (['orgNames', 'orgAcronyms', 'orgExternalIDs', 'ipAddresses', 'top25Vulnerabilities'].includes(header)) { + return `"${domainStatus[header]?.join('|') || []}"` + } + if (header === 'phase') { + switch (domainStatus[header]) { + case 'assess': + return i18n._(t`Assess`) + case 'deploy': + return i18n._(t`Deploy`) + case 'enforce': + return i18n._(t`Enforce`) + case 'maintain': + return i18n._(t`Maintain`) + default: + return '' + } + } + return `"${domainStatus[header]}"` + }) + .join(',') csvOutput += `\n${csvLine}` }) diff --git a/api/src/query.js b/api/src/query.js index df1f2276e..03430825e 100644 --- a/api/src/query.js +++ b/api/src/query.js @@ -1,6 +1,6 @@ -import {GraphQLObjectType} from 'graphql' +import { GraphQLObjectType } from 'graphql' -import {nodeField, nodesField} from './node' +import { nodeField, nodesField } from './node' import * as dmarcSummariesQueries from './dmarc-summaries/queries' import * as domainQueries from './domain/queries' import * as organizationQueries from './organization/queries' @@ -9,6 +9,8 @@ import * as userQueries from './user/queries' import * as verifiedDomainQueries from './verified-domains/queries' import * as verifiedOrgQueries from './verified-organizations/queries' import * as auditLogQueries from './audit-logs/queries' +import * as additionalFindingsQueries from './additional-findings/queries' +import * as tagsQueries from './tags/queries' export const createQuerySchema = () => { return new GraphQLObjectType({ @@ -32,6 +34,8 @@ export const createQuerySchema = () => { ...verifiedDomainQueries, // Verified Organization Queries ...verifiedOrgQueries, + ...additionalFindingsQueries, + ...tagsQueries, }), }) } diff --git a/api/src/scalars/__tests__/scalar-cve-id.test.js b/api/src/scalars/__tests__/scalar-cve-id.test.js new file mode 100644 index 000000000..e63c57fc9 --- /dev/null +++ b/api/src/scalars/__tests__/scalar-cve-id.test.js @@ -0,0 +1,37 @@ +import { CveID } from '../cve-id' +import { GraphQLError } from 'graphql' + +describe('CveID Scalar', () => { + test('valid CVE ID', () => { + const validCveId = 'CVE-2021-12345' + expect(CveID.serialize(validCveId)).toBe(validCveId) + expect(CveID.parseValue(validCveId)).toBe(validCveId) + expect(CveID.parseLiteral({ kind: 'StringValue', value: validCveId })).toBe(validCveId) + }) + + test('invalid CVE ID format', () => { + const invalidCveId = 'INVALID-2021-12345' + expect(() => CveID.serialize(invalidCveId)).toThrow(TypeError) + expect(() => CveID.parseValue(invalidCveId)).toThrow(TypeError) + expect(() => CveID.parseLiteral({ kind: 'StringValue', value: invalidCveId })).toThrow(TypeError) + }) + + test('non-string value', () => { + const nonStringValue = 12345 + expect(() => CveID.serialize(nonStringValue)).toThrow(TypeError) + expect(() => CveID.parseValue(nonStringValue)).toThrow(TypeError) + expect(() => CveID.parseLiteral({ kind: 'IntValue', value: nonStringValue })).toThrow(GraphQLError) + }) + + test('valid CVE ID with different lengths', () => { + const validCveIdShort = 'CVE-2021-1234' + const validCveIdLong = 'CVE-2021-1234567' + expect(CveID.serialize(validCveIdShort)).toBe(validCveIdShort) + expect(CveID.parseValue(validCveIdShort)).toBe(validCveIdShort) + expect(CveID.parseLiteral({ kind: 'StringValue', value: validCveIdShort })).toBe(validCveIdShort) + + expect(CveID.serialize(validCveIdLong)).toBe(validCveIdLong) + expect(CveID.parseValue(validCveIdLong)).toBe(validCveIdLong) + expect(CveID.parseLiteral({ kind: 'StringValue', value: validCveIdLong })).toBe(validCveIdLong) + }) +}) diff --git a/api/src/scalars/__tests__/scalar-domain.test.js b/api/src/scalars/__tests__/scalar-domain.test.js index 5902145c1..515a91114 100644 --- a/api/src/scalars/__tests__/scalar-domain.test.js +++ b/api/src/scalars/__tests__/scalar-domain.test.js @@ -1,6 +1,6 @@ -import {Kind} from 'graphql' -import {stringify} from 'jest-matcher-utils' -import {Domain} from '../index' +import { GraphQLError, Kind } from 'graphql' +import { stringify } from 'jest-matcher-utils' +import { Domain } from '../index' describe('given a domain scalar', () => { describe('serializing inputs', () => { @@ -28,9 +28,7 @@ describe('given a domain scalar', () => { }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when serializing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when serializing ${stringify(invalidInput)}`, () => { expect(() => Domain.serialize(invalidInput)).toThrow( new TypeError(`Value is not a string: ${typeof invalidInput}`), ) @@ -52,16 +50,12 @@ describe('given a domain scalar', () => { }) describe('given an invalid domain', () => { const testDomain = 'not an domain' - expect(() => Domain.parseValue(testDomain)).toThrow( - new TypeError(`Value is not a valid domain: ${testDomain}`), - ) + expect(() => Domain.parseValue(testDomain)).toThrow(new TypeError(`Value is not a valid domain: ${testDomain}`)) }) }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when serializing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when serializing ${stringify(invalidInput)}`, () => { expect(() => Domain.parseValue(invalidInput)).toThrow( new TypeError(`Value is not a string: ${typeof invalidInput}`), ) @@ -110,13 +104,9 @@ describe('given a domain scalar', () => { kind: Kind.DOCUMENT, }, ].forEach((literal) => { - it(`throws an error when parsing invalid literal ${stringify( - literal, - )}`, () => { + it(`throws an error when parsing invalid literal ${stringify(literal)}`, () => { expect(() => Domain.parseLiteral(literal, {})).toThrow( - new TypeError( - `Can only validate strings as domains but got a: ${literal.kind}`, - ), + new GraphQLError(`Can only validate strings as domains but got a: ${literal.kind}`), ) }) }) diff --git a/api/src/scalars/__tests__/scalar-organization-acronym.test.js b/api/src/scalars/__tests__/scalar-organization-acronym.test.js index 0d2a0f218..e9ff9c719 100644 --- a/api/src/scalars/__tests__/scalar-organization-acronym.test.js +++ b/api/src/scalars/__tests__/scalar-organization-acronym.test.js @@ -1,6 +1,6 @@ -import {Kind} from 'graphql' -import {stringify} from 'jest-matcher-utils' -import {Acronym} from '../index' +import { GraphQLError, Kind } from 'graphql' +import { stringify } from 'jest-matcher-utils' +import { Acronym } from '../index' describe('given a acronym scalar', () => { describe('serializing inputs', () => { @@ -22,9 +22,7 @@ describe('given a acronym scalar', () => { }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when serializing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when serializing ${stringify(invalidInput)}`, () => { expect(() => Acronym.serialize(invalidInput)).toThrow( new TypeError(`Value is not string: ${typeof invalidInput}`), ) @@ -47,9 +45,7 @@ describe('given a acronym scalar', () => { }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when serializing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when serializing ${stringify(invalidInput)}`, () => { expect(() => Acronym.parseValue(invalidInput)).toThrow( new TypeError(`Value is not string: ${typeof invalidInput}`), ) @@ -88,13 +84,9 @@ describe('given a acronym scalar', () => { kind: Kind.DOCUMENT, }, ].forEach((literal) => { - it(`throws an error when parsing invalid literal ${stringify( - literal, - )}`, () => { + it(`throws an error when parsing invalid literal ${stringify(literal)}`, () => { expect(() => Acronym.parseLiteral(literal, {})).toThrow( - new TypeError( - `Can only validate strings as acronyms but got a: ${literal.kind}`, - ), + new GraphQLError(`Can only validate strings as acronyms but got a: ${literal.kind}`), ) }) }) diff --git a/api/src/scalars/__tests__/scalar-selector.test.js b/api/src/scalars/__tests__/scalar-selector.test.js index aaefa3c59..d55c22473 100644 --- a/api/src/scalars/__tests__/scalar-selector.test.js +++ b/api/src/scalars/__tests__/scalar-selector.test.js @@ -1,4 +1,4 @@ -import { Kind } from 'graphql' +import { GraphQLError, Kind } from 'graphql' import { stringify } from 'jest-matcher-utils' import { Selectors, SelectorsInput } from '../index' @@ -48,7 +48,7 @@ describe("checking a 'selector' type", () => { ].forEach((literal) => { it(`throws an error when parsing invalid literal ${stringify(literal)}`, () => { expect(() => Selectors.parseLiteral(literal, {})).toThrow( - new TypeError(`Can only validate strings as selectors but got a: ${literal.kind}`), + new GraphQLError(`Can only validate strings as selectors but got a: ${literal.kind}`), ) }) }) diff --git a/api/src/scalars/__tests__/scalar-slug.test.js b/api/src/scalars/__tests__/scalar-slug.test.js index 361ef066a..5a024d33a 100644 --- a/api/src/scalars/__tests__/scalar-slug.test.js +++ b/api/src/scalars/__tests__/scalar-slug.test.js @@ -1,6 +1,6 @@ -import {Kind} from 'graphql' -import {stringify} from 'jest-matcher-utils' -import {Slug} from '../index' +import { GraphQLError, Kind } from 'graphql' +import { stringify } from 'jest-matcher-utils' +import { Slug } from '../index' describe('given a slug scalar', () => { describe('serializing inputs', () => { @@ -15,18 +15,14 @@ describe('given a slug scalar', () => { describe('given an invalid slug', () => { it('throws an error', () => { const testSlug = 'This is an invalid slug' - expect(() => Slug.serialize(testSlug)).toThrow( - new TypeError(`Value is not a valid slug: ${testSlug}`), - ) + expect(() => Slug.serialize(testSlug)).toThrow(new TypeError(`Value is not a valid slug: ${testSlug}`)) }) }) }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when serializing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when serializing ${stringify(invalidInput)}`, () => { expect(() => Slug.serialize(invalidInput)).toThrow( new TypeError(`Value is not string: ${typeof invalidInput}`), ) @@ -46,17 +42,13 @@ describe('given a slug scalar', () => { describe('given an invalid slug', () => { it('throws a type error', () => { const testSlug = 'invalid slug' - expect(() => Slug.parseValue(testSlug)).toThrow( - new TypeError(`Value is not a valid slug: ${testSlug}`), - ) + expect(() => Slug.parseValue(testSlug)).toThrow(new TypeError(`Value is not a valid slug: ${testSlug}`)) }) }) }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when value parsing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when value parsing ${stringify(invalidInput)}`, () => { expect(() => Slug.parseValue(invalidInput)).toThrow( new TypeError(`Value is not string: ${typeof invalidInput}`), ) @@ -96,13 +88,9 @@ describe('given a slug scalar', () => { kind: Kind.DOCUMENT, }, ].forEach((literal) => { - it(`throws an error when parsing invalid literal ${stringify( - literal, - )}`, () => { + it(`throws an error when parsing invalid literal ${stringify(literal)}`, () => { expect(() => Slug.parseLiteral(literal, {})).toThrow( - new TypeError( - `Can only validate strings as slug but got a: ${literal.kind}`, - ), + new GraphQLError(`Can only validate strings as slug but got a: ${literal.kind}`), ) }) }) diff --git a/api/src/scalars/__tests__/scalar-year.test.js b/api/src/scalars/__tests__/scalar-year.test.js index 76437352e..7ac751ed3 100644 --- a/api/src/scalars/__tests__/scalar-year.test.js +++ b/api/src/scalars/__tests__/scalar-year.test.js @@ -1,6 +1,6 @@ -import {Kind} from 'graphql' -import {stringify} from 'jest-matcher-utils' -import {Year} from '../index' +import { GraphQLError, Kind } from 'graphql' +import { stringify } from 'jest-matcher-utils' +import { Year } from '../index' describe('given a year scalar', () => { describe('serializing inputs', () => { @@ -14,17 +14,13 @@ describe('given a year scalar', () => { describe('given an invalid year', () => { it('throws a typeError', () => { const testYear = 'Text' - expect(() => Year.serialize(testYear)).toThrow( - new TypeError(`Value is not a valid year: ${testYear}`), - ) + expect(() => Year.serialize(testYear)).toThrow(new TypeError(`Value is not a valid year: ${testYear}`)) }) }) }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when serializing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when serializing ${stringify(invalidInput)}`, () => { expect(() => Year.serialize(invalidInput)).toThrow( new TypeError(`Value is not string: ${typeof invalidInput}`), ) @@ -43,16 +39,12 @@ describe('given a year scalar', () => { }) describe('given an invalid year', () => { const testYear = 'Text' - expect(() => Year.parseValue(testYear)).toThrow( - new TypeError(`Value is not a valid year: ${testYear}`), - ) + expect(() => Year.parseValue(testYear)).toThrow(new TypeError(`Value is not a valid year: ${testYear}`)) }) }) describe('given invalid inputs', () => { ;[123, {}, [], null, undefined, true].forEach((invalidInput) => { - it(`throws an error when serializing ${stringify( - invalidInput, - )}`, () => { + it(`throws an error when serializing ${stringify(invalidInput)}`, () => { expect(() => Year.parseValue(invalidInput)).toThrow( new TypeError(`Value is not string: ${typeof invalidInput}`), ) @@ -92,13 +84,9 @@ describe('given a year scalar', () => { kind: Kind.DOCUMENT, }, ].forEach((literal) => { - it(`throws an error when parsing invalid literal ${stringify( - literal, - )}`, () => { + it(`throws an error when parsing invalid literal ${stringify(literal)}`, () => { expect(() => Year.parseLiteral(literal, {})).toThrow( - new TypeError( - `Can only validate strings as year but got a: ${literal.kind}`, - ), + new GraphQLError(`Can only validate strings as year but got a: ${literal.kind}`), ) }) }) diff --git a/api/src/scalars/cve-id.js b/api/src/scalars/cve-id.js new file mode 100644 index 000000000..5af501d3c --- /dev/null +++ b/api/src/scalars/cve-id.js @@ -0,0 +1,33 @@ +import { Kind, GraphQLError, GraphQLScalarType } from 'graphql' + +const validate = (value) => { + if (typeof value !== typeof 'string') { + throw new TypeError(`Value is not a string: ${typeof value}`) + } + + const CVE_REGEX = /^CVE-\d{4}-\d{4,7}$/ + + if (!CVE_REGEX.test(value)) { + throw new TypeError(`Value is not a valid CVE ID: ${value}`) + } + + return value +} + +export const CveID = new GraphQLScalarType({ + name: 'CveID', + description: 'String that conforms to a CVE ID structure.', + serialize: validate, + parseValue: validate, + + parseLiteral(ast) { + if (ast.kind !== Kind.STRING) { + throw new GraphQLError(`Can only validate strings as CVE IDs but got a: ${ast.kind}`) + } + return validate(ast.value) + }, +}) + +module.exports = { + CveID, +} diff --git a/api/src/scalars/filter-value.js b/api/src/scalars/filter-value.js new file mode 100644 index 000000000..bb3473bc3 --- /dev/null +++ b/api/src/scalars/filter-value.js @@ -0,0 +1,39 @@ +import { GraphQLScalarType, Kind } from 'graphql' +import { filterEnum } from '../enums/system-filter-value' + +// Convert enum name → value into a plain JS object +const enumValueMap = filterEnum.getValues().reduce((acc, { name, value }) => { + acc[name] = value + return acc +}, {}) + +export const FilterValueScalar = new GraphQLScalarType({ + name: 'FilterValue', + description: 'Filter value: either a system-defined enum name or a user-defined tag string.', + serialize(value) { + return value + }, + parseValue(value) { + if (typeof value !== 'string') { + throw new TypeError(`FilterValue must be a string, got ${typeof value}`) + } + + if (Object.prototype.hasOwnProperty.call(enumValueMap, value)) { + return enumValueMap[value] + } + + return value + }, + parseLiteral(ast) { + if (ast.kind !== Kind.STRING) { + throw new TypeError(`FilterValue must be a string`) + } + + const input = ast.value + if (Object.prototype.hasOwnProperty.call(enumValueMap, input)) { + return enumValueMap[input] + } + + return input + }, +}) diff --git a/api/src/scalars/index.js b/api/src/scalars/index.js index ea5b75022..47f08dd9f 100644 --- a/api/src/scalars/index.js +++ b/api/src/scalars/index.js @@ -1,3 +1,4 @@ +export * from './cve-id' export * from './domain' export * from './organization-acronym' export * from './selector' diff --git a/api/src/server.js b/api/src/server.js index 5cb33d9db..44a3a9274 100644 --- a/api/src/server.js +++ b/api/src/server.js @@ -5,22 +5,24 @@ import { json } from 'body-parser' import http from 'http' import { ApolloServer } from '@apollo/server' -import { expressMiddleware } from '@apollo/server/express4' +import { expressMiddleware } from '@as-integrations/express4' import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default' +import compression from 'compression' import requestLanguage from 'express-request-language' -import { execute, subscribe, GraphQLSchema } from 'graphql' +import { GraphQLSchema } from 'graphql' import depthLimit from 'graphql-depth-limit' import { createComplexityLimitRule } from 'graphql-validation-complexity' -import { SubscriptionServer } from 'subscriptions-transport-ws' import { createQuerySchema } from './query' import { createMutationSchema } from './mutation' +import { FilterValueScalar } from './scalars/filter-value' const createSchema = () => new GraphQLSchema({ query: createQuerySchema(), mutation: createMutationSchema(), + types: [FilterValueScalar], }) const createValidationRules = (maxDepth, complexityCost, scalarCost, objectCost, listFactor) => { @@ -61,10 +63,12 @@ export const Server = async ({ ], }) await server.start() + app.set('trust proxy', true) app.use( '/graphql', cors(), cookieParser(), + compression(), json(), requestLanguage({ languages: ['en', 'fr'], @@ -99,17 +103,5 @@ export const Server = async ({ res.json({ ok: 'yes' }) }) - SubscriptionServer.create( - { - schema, - execute, - subscribe, - }, - { - server: httpServer, - path: server.graphqlPath, - }, - ) - return httpServer } diff --git a/api/src/summaries/data-source.js b/api/src/summaries/data-source.js new file mode 100644 index 000000000..4b534206b --- /dev/null +++ b/api/src/summaries/data-source.js @@ -0,0 +1,7 @@ +import { loadChartSummariesByPeriod } from './loaders' + +export class SummariesDataSource { + constructor({ query, userKey, cleanseInput, i18n }) { + this.getConnectionsByPeriod = loadChartSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + } +} diff --git a/api/src/summaries/index.js b/api/src/summaries/index.js index 7dab4e57a..fd864e4e3 100644 --- a/api/src/summaries/index.js +++ b/api/src/summaries/index.js @@ -1,3 +1,4 @@ export * from './loaders' export * from './objects' export * from './queries' +export * from './data-source' diff --git a/api/src/summaries/loaders/__tests__/load-chart-summaries-by-period.test.js b/api/src/summaries/loaders/__tests__/load-chart-summaries-by-period.test.js new file mode 100644 index 000000000..74827da66 --- /dev/null +++ b/api/src/summaries/loaders/__tests__/load-chart-summaries-by-period.test.js @@ -0,0 +1,51 @@ +import { loadChartSummariesByPeriod } from '../load-chart-summaries-by-period' +import { createI18n } from '../../../create-i18n' + +describe('loadChartSummariesByPeriod', () => { + let query, userKey, cleanseInput + + const i18n = createI18n('en') + + beforeEach(() => { + query = jest.fn() + userKey = 'test-user' + cleanseInput = jest.fn((input) => input) + }) + + it('handles database query errors', async () => { + query.mockRejectedValue(new Error('Database error')) + const loader = loadChartSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + await expect(loader({ startDate: '2023-01-01', endDate: '2023-01-31', sortDirection: 'ASC' })).rejects.toThrow( + 'Unable to load chart summary data. Please try again.', + ) + }) + + it('handles cursor errors', async () => { + query.mockResolvedValue({ + next: jest.fn().mockRejectedValue(new Error('Cursor error')), + }) + const loader = loadChartSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + await expect(loader({ startDate: '2023-01-01', endDate: '2023-01-31', sortDirection: 'ASC' })).rejects.toThrow( + 'Unable to load chart summary data. Please try again.', + ) + }) + + it('returns empty result if no summaries are found', async () => { + query.mockResolvedValue({ + all: jest.fn().mockResolvedValue([]), + }) + const loader = loadChartSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + const result = await loader({ startDate: '2023-01-01', endDate: '2023-01-31', sortDirection: 'ASC' }) + expect(result).toEqual([]) + }) + + it('returns summaries if found', async () => { + const summaries = [{ id: 1, date: '2023-01-01' }] + query.mockResolvedValue({ + all: jest.fn().mockResolvedValue(summaries), + }) + const loader = loadChartSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + const result = await loader({ startDate: '2023-01-01', endDate: '2023-01-31', sortDirection: 'ASC' }) + expect(result).toEqual(summaries) + }) +}) diff --git a/api/src/summaries/loaders/__tests__/load-chart-summary-by-key.test.js b/api/src/summaries/loaders/__tests__/load-chart-summary-by-key.test.js deleted file mode 100644 index 8e873a919..000000000 --- a/api/src/summaries/loaders/__tests__/load-chart-summary-by-key.test.js +++ /dev/null @@ -1,216 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { loadChartSummaryByKey } from '../../index' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given the loadChartSummaryByKey function', () => { - let query, drop, truncate, collections, i18n - - const consoleErrorOutput = [] - const mockedError = (output) => consoleErrorOutput.push(output) - - beforeAll(() => { - console.error = mockedError - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - afterEach(() => { - consoleErrorOutput.length = 0 - }) - - describe('given a successful load', () => { - beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - web: { - total: 1000, - fail: 500, - pass: 500, - }, - mail: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - describe('given a single id', () => { - it('returns a single summary', async () => { - const expectedCursor = await query` - FOR summary IN chartSummaries - SORT summary.date DESC LIMIT 1 - RETURN summary.web - ` - const expectedSummary = await expectedCursor.next() - - const loader = loadChartSummaryByKey({ query, i18n }) - const webSummary = await loader.load('web') - - expect(webSummary).toEqual(expectedSummary) - }) - }) - describe('given multiple ids', () => { - it('returns multiple dkim scans', async () => { - const summaryKeys = [] - const expectedSummaries = [] - const expectedCursor = await query` - FOR summary IN chartSummaries - SORT summary.date DESC LIMIT 1 - RETURN summary - ` - - while (expectedCursor.hasMore) { - const tempSummary = await expectedCursor.next() - summaryKeys.push(tempSummary._key) - expectedSummaries.push(tempSummary) - } - - const loader = loadChartSummaryByKey({ query, i18n }) - const chartSummaries = await loader.loadMany(summaryKeys) - expect(chartSummaries).toEqual(expectedSummaries) - }) - }) - }) - describe('given an unsuccessful load', () => { - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given a database error', () => { - it('raises an error', async () => { - query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) - const loader = loadChartSummaryByKey({ query, userKey: '1234', i18n }) - - try { - await loader.load('1') - } catch (err) { - expect(err).toEqual(new Error('Unable to load summary. Please try again.')) - } - - expect(consoleErrorOutput).toEqual([ - `Database error occurred when user: 1234 running loadChartSummaryByKey: Error: Database error occurred.`, - ]) - }) - }) - describe('given a cursor error', () => { - it('raises an error', async () => { - const cursor = { - next() { - throw new Error('Cursor error occurred.') - }, - } - query = jest.fn().mockReturnValue(cursor) - const loader = loadChartSummaryByKey({ query, userKey: '1234', i18n }) - - try { - await loader.load('1') - } catch (err) { - expect(err).toEqual(new Error('Unable to load summary. Please try again.')) - } - - expect(consoleErrorOutput).toEqual([ - `Cursor error occurred when user: 1234 running loadChartSummaryByKey: Error: Cursor error occurred.`, - ]) - }) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given a database error', () => { - it('raises an error', async () => { - query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) - const loader = loadChartSummaryByKey({ query, userKey: '1234', i18n }) - - try { - await loader.load('1') - } catch (err) { - expect(err).toEqual(new Error('Impossible de charger le résumé. Veuillez réessayer.')) - } - - expect(consoleErrorOutput).toEqual([ - `Database error occurred when user: 1234 running loadChartSummaryByKey: Error: Database error occurred.`, - ]) - }) - }) - describe('given a cursor error', () => { - it('raises an error', async () => { - const cursor = { - next() { - throw new Error('Cursor error occurred.') - }, - } - query = jest.fn().mockReturnValue(cursor) - const loader = loadChartSummaryByKey({ query, userKey: '1234', i18n }) - - try { - await loader.load('1') - } catch (err) { - expect(err).toEqual(new Error('Impossible de charger le résumé. Veuillez réessayer.')) - } - - expect(consoleErrorOutput).toEqual([ - `Cursor error occurred when user: 1234 running loadChartSummaryByKey: Error: Cursor error occurred.`, - ]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/loaders/index.js b/api/src/summaries/loaders/index.js index b6b500338..427499a43 100644 --- a/api/src/summaries/loaders/index.js +++ b/api/src/summaries/loaders/index.js @@ -1,2 +1 @@ -export * from './load-chart-summary-by-key' -export * from './load-chart-summary-connections-by-period' +export * from './load-chart-summaries-by-period' diff --git a/api/src/summaries/loaders/load-chart-summaries-by-period.js b/api/src/summaries/loaders/load-chart-summaries-by-period.js new file mode 100644 index 000000000..3d6e2382b --- /dev/null +++ b/api/src/summaries/loaders/load-chart-summaries-by-period.js @@ -0,0 +1,70 @@ +import { t } from '@lingui/macro' +import { aql } from 'arangojs' + +export const loadChartSummariesByPeriod = + ({ query, userKey, cleanseInput, i18n }) => + async ({ startDate, endDate, sortDirection = 'ASC', limit, scope = 'verified' }) => { + const cleansedStartDate = startDate ? cleanseInput(startDate) : null + const cleansedEndDate = endDate ? cleanseInput(endDate) : new Date().toISOString() + + const filterUniqueDates = (array) => { + const filteredArray = [] + const dateSet = new Set() + array.forEach((item) => { + if (!dateSet.has(item.date)) { + filteredArray.push(item) + dateSet.add(item.date) + } + }) + return filteredArray + } + + const sortString = aql`SORT summary.date ${sortDirection}` + let startDateFilter = aql`` + if (typeof cleansedStartDate !== 'undefined') { + startDateFilter = aql`FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') >= DATE_FORMAT(${cleansedStartDate}, '%yyyy-%mm-%dd')` + } + let endDateFilter = aql`` + if (typeof cleansedEndDate !== 'undefined') { + endDateFilter = aql`FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') <= DATE_FORMAT(${cleansedEndDate}, '%yyyy-%mm-%dd')` + } + let limitString = aql`` + if (typeof limit !== 'undefined') { + limitString = aql`LIMIT ${limit}` + } + + let scopeFilter = aql`FILTER summary.scope == ${scope}` + if (scope === 'verified') { + scopeFilter = aql`FILTER summary.scope == ${scope} OR summary.scope == null` + } + + let requestedSummaryInfo + try { + requestedSummaryInfo = await query` + FOR summary IN chartSummaries + ${scopeFilter} + ${startDateFilter} + ${endDateFilter} + ${sortString} + ${limitString} + RETURN MERGE({ id: summary._key }, DOCUMENT(summary._id)) + ` + } catch (err) { + console.error( + `Database error occurred while user: ${userKey} was trying to gather chart summaries in loadChartSummariesByPeriod, error: ${err}`, + ) + throw new Error(i18n._(t`Unable to load chart summary data. Please try again.`)) + } + + let summariesInfo = [] + try { + summariesInfo = filterUniqueDates(await requestedSummaryInfo.all()) + } catch (err) { + console.error( + `Cursor error occurred while user: ${userKey} was trying to gather chart summaries in loadChartSummariesByPeriod, error: ${err}`, + ) + throw new Error(i18n._(t`Unable to load chart summary data. Please try again.`)) + } + + return summariesInfo + } diff --git a/api/src/summaries/loaders/load-chart-summary-by-key.js b/api/src/summaries/loaders/load-chart-summary-by-key.js deleted file mode 100644 index 719c0162f..000000000 --- a/api/src/summaries/loaders/load-chart-summary-by-key.js +++ /dev/null @@ -1,29 +0,0 @@ -import DataLoader from 'dataloader' -import { t } from '@lingui/macro' - -export const loadChartSummaryByKey = ({ query, userKey, i18n }) => - new DataLoader(async (keys) => { - let cursor - - try { - cursor = await query` - WITH chartSummaries - FOR summary IN chartSummaries - SORT summary.date DESC LIMIT 1 - RETURN summary - ` - } catch (err) { - console.error(`Database error occurred when user: ${userKey} running loadChartSummaryByKey: ${err}`) - throw new Error(i18n._(t`Unable to load summary. Please try again.`)) - } - - let summariesMap - try { - summariesMap = await cursor.next() - } catch (err) { - console.error(`Cursor error occurred when user: ${userKey} running loadChartSummaryByKey: ${err}`) - throw new Error(i18n._(t`Unable to load summary. Please try again.`)) - } - - return keys.map((key) => summariesMap[key]) - }) diff --git a/api/src/summaries/loaders/load-chart-summary-connections-by-period.js b/api/src/summaries/loaders/load-chart-summary-connections-by-period.js deleted file mode 100644 index 78effd2b2..000000000 --- a/api/src/summaries/loaders/load-chart-summary-connections-by-period.js +++ /dev/null @@ -1,166 +0,0 @@ -import { toGlobalId } from 'graphql-relay' -import { t } from '@lingui/macro' - -export const loadChartSummaryConnectionsByPeriod = - ({ query, userKey, cleanseInput, i18n }) => - async ({ period, year }) => { - if (typeof period === 'undefined') { - console.warn(`User: ${userKey} did not have \`period\` argument set for: loadChartSummaryConnectionsByPeriod.`) - throw new Error(i18n._(t`You must provide a \`period\` value to access the \`ChartSummaries\` connection.`)) - } - const cleansedPeriod = cleanseInput(period) - const monthMap = { - january: '01', - february: '02', - march: '03', - april: '04', - may: '05', - june: '06', - july: '07', - august: '08', - september: '09', - october: '10', - november: '11', - december: '12', - } - const periodMonth = monthMap[cleansedPeriod] - - if (typeof year === 'undefined') { - console.warn(`User: ${userKey} did not have \`year\` argument set for: loadChartSummaryConnectionsByPeriod.`) - throw new Error(i18n._(t`You must provide a \`year\` value to access the \`ChartSummaries\` connection.`)) - } - const periodYear = cleanseInput(year) - - let startDate - let requestedSummaryInfo - try { - if (period === 'thirtyDays') { - startDate = new Date(new Date().setDate(new Date().getDate() - 30)) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN chartSummaries - FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') >= DATE_FORMAT(${startDate}, '%yyyy-%mm-%dd') - SORT summary.date ASC - RETURN { - id: summary._key, - date: summary.date, - https: summary.https, - dmarc: summary.dmarc, - } - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } else if (period === 'lastYear') { - startDate = new Date(new Date().setDate(new Date().getDate() - 365)) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN chartSummaries - FILTER DATE_FORMAT(summary.date, '%yyyy-%mm-%dd') >= DATE_FORMAT(${startDate}, '%yyyy-%mm-%dd') - SORT summary.date ASC - RETURN { - id: summary._key, - date: summary.date, - https: summary.https, - dmarc: summary.dmarc, - } - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } else if (period === 'yearToDate') { - startDate = new Date(`${periodYear}-01-01`) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN chartSummaries - FILTER DATE_FORMAT(summary.date, '%yyyy') == DATE_FORMAT(${startDate}, '%yyyy') - SORT summary.date ASC - RETURN { - id: summary._key, - date: summary.date, - https: summary.https, - dmarc: summary.dmarc, - } - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } else { - startDate = new Date(`${periodYear}-${periodMonth}-01`) - requestedSummaryInfo = await query` - LET retrievedSummaries = ( - FOR summary IN chartSummaries - FILTER DATE_FORMAT(summary.date, "%yyyy-%mm") == DATE_FORMAT(${startDate}, "%yyyy-%mm") - SORT summary.date ASC - RETURN { - id: summary._key, - date: summary.date, - https: summary.https, - dmarc: summary.dmarc, - } - ) - - RETURN { - "summaries": retrievedSummaries, - "totalCount": LENGTH(retrievedSummaries), - } - ` - } - } catch (err) { - console.error( - `Database error occurred while user: ${userKey} was trying to gather chart summaries in loadChartSummaryConnectionsByPeriod, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to load chart summary data. Please try again.`)) - } - - let summariesInfo - try { - summariesInfo = await requestedSummaryInfo.next() - } catch (err) { - console.error( - `Cursor error occurred while user: ${userKey} was trying to gather chart summaries in loadChartSummaryConnectionsByPeriod, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to load chart summary data. Please try again.`)) - } - - if (summariesInfo.summaries.length === 0) { - return { - edges: [], - totalCount: 0, - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: '', - endCursor: '', - }, - } - } - - const edges = summariesInfo.summaries.map((summary) => { - summary.startDate = startDate - return { - cursor: toGlobalId('chartSummary', summary.id), - node: summary, - } - }) - - return { - edges, - totalCount: summariesInfo.totalCount, - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: '', - endCursor: '', - }, - } - } diff --git a/api/src/summaries/objects/__tests__/chart-summary-connection.test.js b/api/src/summaries/objects/__tests__/chart-summary-connection.test.js deleted file mode 100644 index cff5c2089..000000000 --- a/api/src/summaries/objects/__tests__/chart-summary-connection.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import { GraphQLInt } from 'graphql' -import { chartSummaryConnection } from '../index' - -describe('given the organization connection object', () => { - describe('testing its field definitions', () => { - it('has a totalCount field', () => { - const demoType = chartSummaryConnection.connectionType.getFields() - - expect(demoType).toHaveProperty('totalCount') - expect(demoType.totalCount.type).toMatchObject(GraphQLInt) - }) - }) - describe('testing its field resolvers', () => { - describe('testing the totalCount resolver', () => { - it('returns the resolved value', () => { - const demoType = chartSummaryConnection.connectionType.getFields() - - expect(demoType.totalCount.resolve({ totalCount: 1 })).toEqual(1) - }) - }) - }) -}) diff --git a/api/src/summaries/objects/chart-summary-connection.js b/api/src/summaries/objects/chart-summary-connection.js deleted file mode 100644 index 2df842cf5..000000000 --- a/api/src/summaries/objects/chart-summary-connection.js +++ /dev/null @@ -1,16 +0,0 @@ -import { GraphQLInt } from 'graphql' -import { connectionDefinitions } from 'graphql-relay' - -import { chartSummaryType } from './chart-summary' - -export const chartSummaryConnection = connectionDefinitions({ - name: 'ChartSummary', - nodeType: chartSummaryType, - connectionFields: () => ({ - totalCount: { - type: GraphQLInt, - description: 'The total amount of dmarc summaries the user has access to.', - resolve: ({ totalCount }) => totalCount, - }, - }), -}) diff --git a/api/src/summaries/objects/chart-summary.js b/api/src/summaries/objects/chart-summary.js index eeb6bdc80..9dda9c600 100644 --- a/api/src/summaries/objects/chart-summary.js +++ b/api/src/summaries/objects/chart-summary.js @@ -3,8 +3,25 @@ import { categorizedSummaryType } from './categorized-summary' import { globalIdField } from 'graphql-relay' import { GraphQLDate } from 'graphql-scalars' +const makeCategory = (key) => ({ + type: categorizedSummaryType, + resolve: (parent) => { + const data = parent[key] + const total = data.total + const safe = total > 0 + return { + total, + categories: [ + { name: 'pass', count: data.pass, percentage: safe ? Number(((data.pass / total) * 100).toFixed(1)) : 0 }, + { name: 'fail', count: data.fail, percentage: safe ? Number(((data.fail / total) * 100).toFixed(1)) : 0 }, + ], + } + }, +}) + export const chartSummaryType = new GraphQLObjectType({ name: 'ChartSummary', + description: `This object contains the information for each type of summary that has been pre-computed`, fields: () => ({ id: globalIdField('chartSummary'), date: { @@ -12,70 +29,33 @@ export const chartSummaryType = new GraphQLObjectType({ description: 'Date that the summary was computed.', resolve: ({ date }) => date, }, - https: { - type: categorizedSummaryType, - description: 'https summary data', - resolve: ({ https }) => { - let percentPass, percentageFail - if (https.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((https.pass / https.total) * 100).toFixed(1)) - percentageFail = Number(((https.fail / https.total) * 100).toFixed(1)) - } - - const categories = [ - { - name: 'pass', - count: https.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: https.fail, - percentage: percentageFail, - }, - ] - - return { - categories, - total: https.total, - } - }, + https: { ...makeCategory('https'), description: 'https summary data' }, + dmarc: { ...makeCategory('dmarc'), description: 'dmarc summary data' }, + mail: { ...makeCategory('mail'), description: 'Summary based on mail scan results for all domains.' }, + web: { ...makeCategory('web'), description: 'Summary based on web scan results for all domains.' }, + ssl: { ...makeCategory('ssl'), description: 'Summary based on SSL scan results for all domains.' }, + webConnections: { + ...makeCategory('web_connections'), + description: 'Summary based on HTTPS and HSTS scan results for all domains.', }, - dmarc: { + spf: { ...makeCategory('spf'), description: 'Summary based on SPF scan results for all domains.' }, + dkim: { ...makeCategory('dkim'), description: 'Summary based on DKIM scan results for all domains.' }, + dmarcPhase: { type: categorizedSummaryType, - description: 'dmarc summary data', - resolve: ({ dmarc }) => { - let percentPass, percentageFail - if (dmarc.total <= 0) { - percentPass = 0 - percentageFail = 0 - } else { - percentPass = Number(((dmarc.pass / dmarc.total) * 100).toFixed(1)) - percentageFail = Number(((dmarc.fail / dmarc.total) * 100).toFixed(1)) - } - - const categories = [ - { - name: 'pass', - count: dmarc.pass, - percentage: percentPass, - }, - { - name: 'fail', - count: dmarc.fail, - percentage: percentageFail, - }, - ] - + description: 'Summary based on DMARC phases for all domains.', + resolve: ({ dmarc_phase: dmarcPhase }) => { + const total = dmarcPhase.total + const safe = total > 0 + const phaseNames = ['assess', 'deploy', 'enforce', 'maintain'] return { - categories, - total: dmarc.total, + total, + categories: phaseNames.map((name) => ({ + name, + count: dmarcPhase[name], + percentage: safe ? Number(((dmarcPhase[name] / total) * 100).toFixed(1)) : 0, + })), } }, }, }), - description: `This object contains the information for each type of summary that has been pre-computed`, }) diff --git a/api/src/summaries/objects/index.js b/api/src/summaries/objects/index.js index f7d522f25..e4f175275 100644 --- a/api/src/summaries/objects/index.js +++ b/api/src/summaries/objects/index.js @@ -1,4 +1,3 @@ export * from './categorized-summary' export * from './summary-category' export * from './chart-summary' -export * from './chart-summary-connection' diff --git a/api/src/summaries/queries/__tests__/dkim-summary.test.js b/api/src/summaries/queries/__tests__/dkim-summary.test.js deleted file mode 100644 index a5aa0f7e8..000000000 --- a/api/src/summaries/queries/__tests__/dkim-summary.test.js +++ /dev/null @@ -1,172 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given dkimSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful dkim summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - dkim: { total: 1000, fail: 500, pass: 500 }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns dkim summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - dkimSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - dkimSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful dkim summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - dkimSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load DKIM summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve DKIM summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/dmarc-phase-summary.test.js b/api/src/summaries/queries/__tests__/dmarc-phase-summary.test.js deleted file mode 100644 index 49202b900..000000000 --- a/api/src/summaries/queries/__tests__/dmarc-phase-summary.test.js +++ /dev/null @@ -1,246 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given dmarcPhaseSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(async () => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - - beforeEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful dmarc phase summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - dmarc_phase: { - not_implemented: 200, - assess: 200, - deploy: 200, - enforce: 200, - maintain: 200, - total: 1000, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns dmarc phase summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - dmarcPhaseSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - dmarcPhaseSummary: { - total: 1000, - categories: [ - { - name: 'not implemented', - count: 200, - percentage: 20, - }, - { - name: 'assess', - count: 200, - percentage: 20, - }, - { - name: 'deploy', - count: 200, - percentage: 20, - }, - { - name: 'enforce', - count: 200, - percentage: 20, - }, - { - name: 'maintain', - count: 200, - percentage: 20, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful dmarc phase summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - dmarcPhaseSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load DMARC phase summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve DMARC phase summary.`]) - }) - }) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful dmarc phase summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - dmarcPhaseSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError('Impossible de charger le résumé DMARC. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve DMARC phase summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/dmarc-summary.test.js b/api/src/summaries/queries/__tests__/dmarc-summary.test.js deleted file mode 100644 index 8451287d8..000000000 --- a/api/src/summaries/queries/__tests__/dmarc-summary.test.js +++ /dev/null @@ -1,176 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given dmarcSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful dmarc summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - dmarc: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns dmarc summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - dmarcSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - dmarcSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful dmarc summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - dmarcSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load DMARC summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve DMARC summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/find-chart-summaries.test.js b/api/src/summaries/queries/__tests__/find-chart-summaries.test.js index 8805dcb8c..81455dd06 100644 --- a/api/src/summaries/queries/__tests__/find-chart-summaries.test.js +++ b/api/src/summaries/queries/__tests__/find-chart-summaries.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -10,7 +11,7 @@ import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadChartSummaryConnectionsByPeriod } from '../../loaders' +import { loadChartSummariesByPeriod } from '../../loaders' import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -54,7 +55,6 @@ describe('given findMyOrganizationsQuery', () => { user = await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: true, }) sum1 = await collections.chartSummaries.save({ @@ -117,32 +117,27 @@ describe('given findMyOrganizationsQuery', () => { schema, source: ` query { - findChartSummaries(month: JANUARY, year: "2021") { - edges { - node { - date - dmarc { - categories { - count - name - percentage - } - total - } - https { - total - categories { - count - name - percentage - } - } - id + findChartSummaries(startDate: "2021-01-01", endDate: "2021-01-31", sortDirection: ASC) { + date + dmarc { + categories { + count + name + percentage } + total + } + https { + total + categories { + count + name + percentage } - totalCount - } + } + id } + } `, rootValue: null, contextValue: { @@ -160,97 +155,91 @@ describe('given findMyOrganizationsQuery', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadChartSummaryConnectionsByPeriod: loadChartSummaryConnectionsByPeriod({ + dataSources: { + summaries: { getConnectionsByPeriod: loadChartSummariesByPeriod({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', - }), + }) }, }, }, }) const expectedResponse = { data: { - findChartSummaries: { - edges: [ - { - node: { - id: toGlobalId('chartSummary', sum1._key), - date: '2021-01-01', - https: { - total: 1, - categories: [ - { - count: 1, - name: 'pass', - percentage: 100, - }, - { - count: 0, - name: 'fail', - percentage: 0, - }, - ], + findChartSummaries: [ + { + id: toGlobalId('chartSummary', sum1._key), + date: '2021-01-01', + https: { + total: 1, + categories: [ + { + count: 1, + name: 'pass', + percentage: 100, }, - dmarc: { - total: 1, - categories: [ - { - count: 1, - name: 'pass', - percentage: 100, - }, - { - count: 0, - name: 'fail', - percentage: 0, - }, - ], + { + count: 0, + name: 'fail', + percentage: 0, }, - }, + ], }, - { - node: { - id: toGlobalId('chartSummary', sum2._key), - date: '2021-01-02', - https: { - total: 2, - categories: [ - { - count: 1, - name: 'pass', - percentage: 50, - }, - { - count: 1, - name: 'fail', - percentage: 50, - }, - ], + dmarc: { + total: 1, + categories: [ + { + count: 1, + name: 'pass', + percentage: 100, }, - dmarc: { - total: 2, - categories: [ - { - count: 2, - name: 'pass', - percentage: 100, - }, - { - count: 0, - name: 'fail', - percentage: 0, - }, - ], + { + count: 0, + name: 'fail', + percentage: 0, }, - }, + ], }, - ], - totalCount: 2, - }, + }, + + { + id: toGlobalId('chartSummary', sum2._key), + date: '2021-01-02', + https: { + total: 2, + categories: [ + { + count: 1, + name: 'pass', + percentage: 50, + }, + { + count: 1, + name: 'fail', + percentage: 50, + }, + ], + }, + dmarc: { + total: 2, + categories: [ + { + count: 2, + name: 'pass', + percentage: 100, + }, + { + count: 0, + name: 'fail', + percentage: 0, + }, + ], + }, + }, + ], }, } expect(response).toEqual(expectedResponse) @@ -285,32 +274,27 @@ describe('given findMyOrganizationsQuery', () => { schema, source: ` query { - findChartSummaries(month: JANUARY, year: "2021") { - edges { - node { - date - dmarc { - categories { - count - name - percentage - } - total - } - https { - total - categories { - count - name - percentage - } - } - id + findChartSummaries(startDate: "2021-01-01", endDate: "2021-01-31", sortDirection: ASC) { + date + dmarc { + categories { + count + name + percentage } + total + } + https { + total + categories { + count + name + percentage } - totalCount - } + } + id } + } `, rootValue: null, contextValue: { @@ -321,15 +305,15 @@ describe('given findMyOrganizationsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadChartSummaryConnectionsByPeriod: loadChartSummaryConnectionsByPeriod({ + dataSources: { + summaries: { getConnectionsByPeriod: loadChartSummariesByPeriod({ query: mockedQuery, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', i18n, - }), + }) }, }, }, }) @@ -338,7 +322,7 @@ describe('given findMyOrganizationsQuery', () => { expect(response.errors).toEqual(error) expect(consoleOutput).toEqual([ - `Database error occurred while user: ${user._key} was trying to gather chart summaries in loadChartSummaryConnectionsByPeriod, error: Error: Database error occurred.`, + `Database error occurred while user: ${user._key} was trying to gather chart summaries in loadChartSummariesByPeriod, error: Error: Database error occurred.`, ]) }) }) diff --git a/api/src/summaries/queries/__tests__/https-summary.test.js b/api/src/summaries/queries/__tests__/https-summary.test.js deleted file mode 100644 index 8870b362a..000000000 --- a/api/src/summaries/queries/__tests__/https-summary.test.js +++ /dev/null @@ -1,228 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given httpsSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(async () => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - - beforeEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful https summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - https: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns https summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - httpsSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - httpsSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful https summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - httpsSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load HTTPS summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve HTTPS summary.`]) - }) - }) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful https summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - httpsSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError('Impossible de charger le résumé HTTPS. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve HTTPS summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/mail-summary.test.js b/api/src/summaries/queries/__tests__/mail-summary.test.js deleted file mode 100644 index 713583132..000000000 --- a/api/src/summaries/queries/__tests__/mail-summary.test.js +++ /dev/null @@ -1,228 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given mailSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(async () => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - - beforeEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful mail summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - mail: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns mail summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - mailSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - mailSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful mail summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - mailSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load mail summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve mail summary.`]) - }) - }) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful mail summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - mailSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError('Impossible de charger le résumé du courrier. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve mail summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/spf-summary.test.js b/api/src/summaries/queries/__tests__/spf-summary.test.js deleted file mode 100644 index 393ed8cf6..000000000 --- a/api/src/summaries/queries/__tests__/spf-summary.test.js +++ /dev/null @@ -1,176 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given spfSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful spf summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - spf: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns spf summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - spfSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - spfSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful spf summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - spfSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load SPF summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve SPF summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/ssl-summary.test.js b/api/src/summaries/queries/__tests__/ssl-summary.test.js deleted file mode 100644 index 38d3c70d9..000000000 --- a/api/src/summaries/queries/__tests__/ssl-summary.test.js +++ /dev/null @@ -1,176 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given sslSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful ssl summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - ssl: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns ssl summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - sslSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - sslSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful ssl summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - sslSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load SSL summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve SSL summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/web-connections-summary.test.js b/api/src/summaries/queries/__tests__/web-connections-summary.test.js deleted file mode 100644 index 9bcd2c92f..000000000 --- a/api/src/summaries/queries/__tests__/web-connections-summary.test.js +++ /dev/null @@ -1,176 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given webConnectionsSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful webConnections summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2021-01-01', - web_connections: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns webConnections summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - webConnectionsSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - webConnectionsSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful webConnections summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - webConnectionsSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load web connections summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve web connections summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/__tests__/web-summary.test.js b/api/src/summaries/queries/__tests__/web-summary.test.js deleted file mode 100644 index d3045051f..000000000 --- a/api/src/summaries/queries/__tests__/web-summary.test.js +++ /dev/null @@ -1,227 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { loadChartSummaryByKey } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('given webSummary query', () => { - let query, drop, truncate, schema, collections, i18n - - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) - beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('given successful web summary retrieval', () => { - beforeAll(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - beforeEach(async () => { - await collections.chartSummaries.save({ - date: '2020-10-02', - web: { - total: 1000, - fail: 500, - pass: 500, - }, - }) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - it('returns web summary', async () => { - const response = await graphql({ - schema, - source: ` - query { - webSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: loadChartSummaryByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - webSummary: { - total: 1000, - categories: [ - { - name: 'pass', - count: 500, - percentage: 50, - }, - { - name: 'fail', - count: 500, - percentage: 50, - }, - ], - }, - }, - } - expect(response).toEqual(expectedResponse) - }) - }) - - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful web summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - webSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError(`Unable to load web summary. Please try again.`)] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve web summary.`]) - }) - }) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('given unsuccessful web summary retrieval', () => { - describe('summary cannot be found', () => { - it('returns an appropriate error message', async () => { - const response = await graphql({ - schema, - source: ` - query { - webSummary { - total - categories { - name - count - percentage - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - loaders: { - loadChartSummaryByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = [new GraphQLError('Impossible de charger le résumé web. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([`User could not retrieve web summary.`]) - }) - }) - }) - }) -}) diff --git a/api/src/summaries/queries/dkim-summary.js b/api/src/summaries/queries/dkim-summary.js deleted file mode 100644 index 9a91978ce..000000000 --- a/api/src/summaries/queries/dkim-summary.js +++ /dev/null @@ -1,33 +0,0 @@ -import { categorizedSummaryType } from '../objects' -import { t } from '@lingui/macro' - -export const dkimSummary = { - type: categorizedSummaryType, - description: 'DKIM summary computed values, used to build summary cards.', - resolve: async (_, __, { i18n, loaders: { loadChartSummaryByKey } }) => { - const summary = await loadChartSummaryByKey.load('dkim') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve DKIM summary.`) - throw new Error(i18n._(t`Unable to load DKIM summary. Please try again.`)) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/dmarc-phase-summary.js b/api/src/summaries/queries/dmarc-phase-summary.js deleted file mode 100644 index 1fdf19078..000000000 --- a/api/src/summaries/queries/dmarc-phase-summary.js +++ /dev/null @@ -1,57 +0,0 @@ -import {categorizedSummaryType} from '../objects' -import {t} from '@lingui/macro' - -export const dmarcPhaseSummary = { - type: categorizedSummaryType, - description: - 'DMARC phase summary computed values, used to build summary cards.', - resolve: async (_, __, {i18n, loaders: {loadChartSummaryByKey}}) => { - const summary = await loadChartSummaryByKey.load('dmarc_phase') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve DMARC phase summary.`) - throw new Error( - i18n._(t`Unable to load DMARC phase summary. Please try again.`), - ) - } - - const categories = [ - { - name: 'not implemented', - count: summary.not_implemented, - percentage: Number( - ((summary.not_implemented / summary.total) * 100).toFixed(1), - ), - }, - { - name: 'assess', - count: summary.assess, - percentage: Number(((summary.assess / summary.total) * 100).toFixed(1)), - }, - { - name: 'deploy', - count: summary.deploy, - percentage: Number(((summary.deploy / summary.total) * 100).toFixed(1)), - }, - { - name: 'enforce', - count: summary.enforce, - percentage: Number( - ((summary.enforce / summary.total) * 100).toFixed(1), - ), - }, - { - name: 'maintain', - count: summary.maintain, - percentage: Number( - ((summary.maintain / summary.total) * 100).toFixed(1), - ), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/dmarc-summary.js b/api/src/summaries/queries/dmarc-summary.js deleted file mode 100644 index e7ca64b3c..000000000 --- a/api/src/summaries/queries/dmarc-summary.js +++ /dev/null @@ -1,33 +0,0 @@ -import { categorizedSummaryType } from '../objects' -import { t } from '@lingui/macro' - -export const dmarcSummary = { - type: categorizedSummaryType, - description: 'DMARC summary computed values, used to build summary cards.', - resolve: async (_, __, { i18n, loaders: { loadChartSummaryByKey } }) => { - const summary = await loadChartSummaryByKey.load('dmarc') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve DMARC summary.`) - throw new Error(i18n._(t`Unable to load DMARC summary. Please try again.`)) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/find-chart-summaries.js b/api/src/summaries/queries/find-chart-summaries.js index f7cf6734a..42755f36c 100644 --- a/api/src/summaries/queries/find-chart-summaries.js +++ b/api/src/summaries/queries/find-chart-summaries.js @@ -1,43 +1,46 @@ -import { GraphQLNonNull } from 'graphql' +import { GraphQLList, GraphQLString, GraphQLInt } from 'graphql' -import { Year } from '../../scalars' -import { PeriodEnums } from '../../enums' -import { chartSummaryConnection } from '../objects' +import { chartSummaryType } from '../objects' +import { ChartSummaryScopeEnums, OrderDirection } from '../../enums' export const findChartSummaries = { - type: chartSummaryConnection.connectionType, - description: 'Select domains a user has access to.', + type: new GraphQLList(chartSummaryType), + description: 'Select chart summaries a user has access to.', args: { - month: { - type: new GraphQLNonNull(PeriodEnums), - description: 'The month in which the returned data is relevant to.', + startDate: { + type: GraphQLString, + description: 'The start date for the returned data (YYYY-MM-DD).', }, - year: { - type: new GraphQLNonNull(Year), - description: 'The year in which the returned data is relevant to.', + endDate: { + type: GraphQLString, + description: 'The end date for the returned data (YYYY-MM-DD).', + }, + sortDirection: { + type: OrderDirection, + description: 'The direction in which to sort the data (ASC or DESC).', + }, + limit: { + type: GraphQLInt, + description: 'The maximum amount of summaries to be returned.', + }, + scope: { + type: ChartSummaryScopeEnums, + description: 'The set of organizations the returned summaries should cover. Defaults to verified.', }, }, resolve: async ( _, args, - { - userKey, - auth: { userRequired, loginRequiredBool, verifiedRequired }, - loaders: { loadChartSummaryConnectionsByPeriod }, - }, + { userKey, auth: { userRequired, loginRequiredBool, verifiedRequired }, dataSources: { summaries } }, ) => { if (loginRequiredBool) { const user = await userRequired() verifiedRequired({ user }) } - const summaryConnections = await loadChartSummaryConnectionsByPeriod({ - period: args.month, - ...args, - }) + const summaryConnections = await summaries.getConnectionsByPeriod({ ...args }) console.info(`User: ${userKey} successfully retrieved their chart summaries.`) - return summaryConnections }, } diff --git a/api/src/summaries/queries/https-summary.js b/api/src/summaries/queries/https-summary.js deleted file mode 100644 index 781a23ab8..000000000 --- a/api/src/summaries/queries/https-summary.js +++ /dev/null @@ -1,35 +0,0 @@ -import {categorizedSummaryType} from '../objects' -import {t} from '@lingui/macro' - -export const httpsSummary = { - type: categorizedSummaryType, - description: 'HTTPS summary computed values, used to build summary cards.', - resolve: async (_, __, {i18n, loaders: {loadChartSummaryByKey}}) => { - const summary = await loadChartSummaryByKey.load('https') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve HTTPS summary.`) - throw new Error( - i18n._(t`Unable to load HTTPS summary. Please try again.`), - ) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/index.js b/api/src/summaries/queries/index.js index 50087ebee..800203001 100644 --- a/api/src/summaries/queries/index.js +++ b/api/src/summaries/queries/index.js @@ -1,10 +1 @@ -export * from './dkim-summary' -export * from './dmarc-phase-summary' -export * from './dmarc-summary' -export * from './https-summary' -export * from './mail-summary' -export * from './spf-summary' -export * from './ssl-summary' -export * from './web-connections-summary' -export * from './web-summary' export * from './find-chart-summaries' diff --git a/api/src/summaries/queries/mail-summary.js b/api/src/summaries/queries/mail-summary.js deleted file mode 100644 index 71f46a8bf..000000000 --- a/api/src/summaries/queries/mail-summary.js +++ /dev/null @@ -1,33 +0,0 @@ -import {categorizedSummaryType} from '../objects' -import {t} from '@lingui/macro' - -export const mailSummary = { - type: categorizedSummaryType, - description: 'Email summary computed values, used to build summary cards.', - resolve: async (_, __, {i18n, loaders: {loadChartSummaryByKey}}) => { - const summary = await loadChartSummaryByKey.load('mail') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve mail summary.`) - throw new Error(i18n._(t`Unable to load mail summary. Please try again.`)) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/spf-summary.js b/api/src/summaries/queries/spf-summary.js deleted file mode 100644 index c320ff40f..000000000 --- a/api/src/summaries/queries/spf-summary.js +++ /dev/null @@ -1,33 +0,0 @@ -import { categorizedSummaryType } from '../objects' -import { t } from '@lingui/macro' - -export const spfSummary = { - type: categorizedSummaryType, - description: 'SPF summary computed values, used to build summary cards.', - resolve: async (_, __, { i18n, loaders: { loadChartSummaryByKey } }) => { - const summary = await loadChartSummaryByKey.load('spf') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve SPF summary.`) - throw new Error(i18n._(t`Unable to load SPF summary. Please try again.`)) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/ssl-summary.js b/api/src/summaries/queries/ssl-summary.js deleted file mode 100644 index ceafecefd..000000000 --- a/api/src/summaries/queries/ssl-summary.js +++ /dev/null @@ -1,33 +0,0 @@ -import { categorizedSummaryType } from '../objects' -import { t } from '@lingui/macro' - -export const sslSummary = { - type: categorizedSummaryType, - description: 'SSL summary computed values, used to build summary cards.', - resolve: async (_, __, { i18n, loaders: { loadChartSummaryByKey } }) => { - const summary = await loadChartSummaryByKey.load('ssl') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve SSL summary.`) - throw new Error(i18n._(t`Unable to load SSL summary. Please try again.`)) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/web-connections-summary.js b/api/src/summaries/queries/web-connections-summary.js deleted file mode 100644 index a25acc7d3..000000000 --- a/api/src/summaries/queries/web-connections-summary.js +++ /dev/null @@ -1,33 +0,0 @@ -import { categorizedSummaryType } from '../objects' -import { t } from '@lingui/macro' - -export const webConnectionsSummary = { - type: categorizedSummaryType, - description: 'Web connections (HTTPS + HSTS) summary computed values, used to build summary cards.', - resolve: async (_, __, { i18n, loaders: { loadChartSummaryByKey } }) => { - const summary = await loadChartSummaryByKey.load('web_connections') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve web connections summary.`) - throw new Error(i18n._(t`Unable to load web connections summary. Please try again.`)) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/summaries/queries/web-summary.js b/api/src/summaries/queries/web-summary.js deleted file mode 100644 index 63347617b..000000000 --- a/api/src/summaries/queries/web-summary.js +++ /dev/null @@ -1,34 +0,0 @@ -import {t} from '@lingui/macro' - -import {categorizedSummaryType} from '../objects' - -export const webSummary = { - type: categorizedSummaryType, - description: 'Web summary computed values, used to build summary cards.', - resolve: async (_, __, {i18n, loaders: {loadChartSummaryByKey}}) => { - const summary = await loadChartSummaryByKey.load('web') - - if (typeof summary === 'undefined') { - console.warn(`User could not retrieve web summary.`) - throw new Error(i18n._(t`Unable to load web summary. Please try again.`)) - } - - const categories = [ - { - name: 'pass', - count: summary.pass, - percentage: Number(((summary.pass / summary.total) * 100).toFixed(1)), - }, - { - name: 'fail', - count: summary.fail, - percentage: Number(((summary.fail / summary.total) * 100).toFixed(1)), - }, - ] - - return { - categories, - total: summary.total, - } - }, -} diff --git a/api/src/tags/data-source.js b/api/src/tags/data-source.js new file mode 100644 index 000000000..5199e29c9 --- /dev/null +++ b/api/src/tags/data-source.js @@ -0,0 +1,106 @@ +import { t } from '@lingui/macro' +import { loadAllTags, loadTagByTagId, loadTagsByOrg } from './loaders' + +export class TagsDataSource { + constructor({ query, userKey, i18n, language, transaction, collections }) { + this._query = query + this._userKey = userKey + this._i18n = i18n + this._transaction = transaction + this._collections = collections + this.all = loadAllTags({ query, userKey, i18n, language }) + this.byTagId = loadTagByTagId({ query, userKey, i18n, language }) + this.byOrg = loadTagsByOrg({ query, userKey, i18n, language }) + } + + // Fetch the raw DB document (label/description as {en, fr} objects, not translated strings). + // Used by mutations that need to read current field values before updating. + async getRaw(tagId) { + const { _query, _userKey, _i18n } = this + let cursor + try { + cursor = await _query` + WITH tags + FOR tag IN tags + FILTER tag.tagId == ${tagId} + RETURN tag + ` + } catch (err) { + console.error(`Database error occurred while retrieving tag: ${tagId} for user: ${_userKey}, err: ${err}`) + throw new Error(_i18n._(t`Unable to update tag. Please try again.`)) + } + try { + return await cursor.next() + } catch (err) { + console.error(`Cursor error occurred while retrieving tag: ${tagId} for user: ${_userKey}, err: ${err}`) + throw new Error(_i18n._(t`Unable to update tag. Please try again.`)) + } + } + + // Insert or update a tag, then return the freshly loaded record. + async create(tag) { + const { _query, _userKey, _i18n, _transaction, _collections } = this + const trx = await _transaction(_collections) + try { + await trx.step( + () => + _query` + UPSERT { tagId: ${tag.tagId} } + INSERT ${tag} + UPDATE ${tag} + IN tags + RETURN NEW + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${_userKey} when inserting new tag: ${err}`) + await trx.abort() + throw new Error(_i18n._(t`Unable to create tag. Please try again.`)) + } + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${_userKey} was creating tag: ${err}`) + await trx.abort() + throw new Error(_i18n._(t`Unable to create tag. Please try again.`)) + } + this.byTagId.clear(tag.tagId) + return this.byTagId.load(tag.tagId) + } + + // Update an existing tag matched by matchTagId (which may differ from tag.tagId after a label rename), + // then return the freshly loaded record. + async save(matchTagId, tag) { + const { _query, _userKey, _i18n, _transaction, _collections } = this + const trx = await _transaction(_collections) + try { + await trx.step( + async () => + await _query` + WITH tags + UPSERT { tagId: ${matchTagId} } + INSERT ${tag} + UPDATE ${tag} + IN tags + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: ${_userKey} attempted to update tag: ${matchTagId}, error: ${err}`, + ) + await trx.abort() + throw new Error(_i18n._(t`Unable to update tag. Please try again.`)) + } + try { + await trx.commit() + } catch (err) { + console.error( + `Transaction commit error occurred when user: ${_userKey} attempted to update tag: ${matchTagId}, error: ${err}`, + ) + await trx.abort() + throw new Error(_i18n._(t`Unable to update tag. Please try again.`)) + } + this.byTagId.clear(tag.tagId) + return this.byTagId.load(tag.tagId) + } +} diff --git a/api/src/tags/index.js b/api/src/tags/index.js new file mode 100644 index 000000000..64ebb55b9 --- /dev/null +++ b/api/src/tags/index.js @@ -0,0 +1,6 @@ +export * from './data-source' +export * from './loaders' +export * from './mutations' +export * from './objects' +export * from './queries' +export * from './unions' diff --git a/api/src/tags/loaders/__tests__/load-all-tags.test.js b/api/src/tags/loaders/__tests__/load-all-tags.test.js new file mode 100644 index 000000000..af52391f7 --- /dev/null +++ b/api/src/tags/loaders/__tests__/load-all-tags.test.js @@ -0,0 +1,180 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' + +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import { loadAllTags } from '../index' +import dbschema from '../../../../database.json' + +const { DB_PASS: rootPass, DB_URL: url } = process.env + +describe('given a loadAllTags dataloader', () => { + let query, drop, truncate, collections, i18n + + const consoleOutput = [] + const mockedError = (output) => consoleOutput.push(output) + beforeAll(() => { + console.error = mockedError + }) + afterEach(() => { + consoleOutput.length = 0 + }) + + describe('given a successful load', () => { + beforeAll(async () => { + ;({ query, drop, truncate, collections } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) + }) + beforeEach(async () => { + await collections.tags.save({ + tagId: 'web', + label: { en: 'Web', fr: 'Web' }, + description: { en: '', fr: '' }, + visible: false, + ownership: 'global', + }) + await collections.tags.save({ + tagId: 'new', + label: { en: 'New', fr: 'Nouveau' }, + description: { en: '', fr: '' }, + visible: true, + ownership: 'global', + }) + }) + afterEach(async () => { + await truncate() + }) + afterAll(async () => { + await drop() + }) + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + it('returns only visible tags', async () => { + // Get User From db + const expectedCursor = await query` + FOR tag IN tags + FILTER tag.visible == true + RETURN { + "tagId": tag.tagId, + "label": TRANSLATE('en', tag.label), + "description": TRANSLATE('en', tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + const expectedTags = await expectedCursor.all() + + const loader = loadAllTags({ query, language: 'en', i18n }) + const tags = await loader({ isVisible: true }) + + expect(tags).toEqual(expectedTags) + }) + it('returns a list of tags', async () => { + const expectedCursor = await query` + FOR tag IN tags + LET label = TRANSLATE('en', tag.label) + SORT label ASC + RETURN { + "tagId": tag.tagId, + "label": label, + "description": TRANSLATE('en', tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + const expectedTags = await expectedCursor.all() + + const loader = loadAllTags({ query, language: 'en', i18n }) + const tags = await loader({ isVisible: false }) + + expect(tags).toEqual(expectedTags) + }) + }) + describe('given an unsuccessful load', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + describe('database error is raised', () => { + it('returns an error', async () => { + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadAllTags({ + query: mockedQuery, + language: 'en', + userKey: '1234', + i18n, + }) + + try { + await loader({ isVisible: false }) + } catch (err) { + expect(err).toEqual(new Error('Unable to load tag(s). Please try again.')) + } + + expect(consoleOutput).toEqual([ + `Database error occurred while user: 1234 was trying to query tags in loadAllTags, Error: Database error occurred.`, + ]) + }) + }) + describe('cursor error is raised', () => { + it('returns an error', async () => { + const cursor = { + all() { + throw new Error('Cursor error occurred.') + }, + } + const mockedQuery = jest.fn().mockReturnValue(cursor) + const loader = loadAllTags({ + query: mockedQuery, + language: 'en', + userKey: '1234', + i18n, + }) + + try { + await loader({ isVisible: false }) + } catch (err) { + expect(err).toEqual(new Error('Unable to load tag(s). Please try again.')) + } + + expect(consoleOutput).toEqual([ + `Cursor error occurred while user: 1234 was trying to gather tags in loadAllTags, Error: Cursor error occurred.`, + ]) + }) + }) + }) +}) diff --git a/api/src/tags/loaders/__tests__/load-tag-by-tag-id.test.js b/api/src/tags/loaders/__tests__/load-tag-by-tag-id.test.js new file mode 100644 index 000000000..fedc4d929 --- /dev/null +++ b/api/src/tags/loaders/__tests__/load-tag-by-tag-id.test.js @@ -0,0 +1,188 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' + +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import { loadTagByTagId } from '../index' +import dbschema from '../../../../database.json' + +const { DB_PASS: rootPass, DB_URL: url } = process.env + +describe('given a loadTagByTagId dataloader', () => { + let query, drop, truncate, collections, i18n + + const consoleOutput = [] + const mockedError = (output) => consoleOutput.push(output) + beforeAll(() => { + console.error = mockedError + }) + afterEach(() => { + consoleOutput.length = 0 + }) + + describe('given a successful load', () => { + beforeAll(async () => { + ;({ query, drop, truncate, collections } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) + }) + beforeEach(async () => { + await collections.tags.save({ + tagId: 'web', + label: { en: 'Web', fr: 'Web' }, + description: { en: '', fr: '' }, + visible: false, + ownership: 'global', + }) + await collections.tags.save({ + tagId: 'new', + label: { en: 'New', fr: 'Nouveau' }, + description: { en: '', fr: '' }, + visible: true, + ownership: 'global', + }) + }) + afterEach(async () => { + await truncate() + }) + afterAll(async () => { + await drop() + }) + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + describe('provided a single id', () => { + it('returns a single tag', async () => { + // Get User From db + const expectedCursor = await query` + FOR tag IN tags + FILTER tag.tagId == 'new' + RETURN { + _type: "tag", + "tagId": tag.tagId, + "label": TRANSLATE('en', tag.label), + "description": TRANSLATE('en', tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + const expectedTag = await expectedCursor.next() + + const loader = loadTagByTagId({ query, language: 'en', i18n }) + const tag = await loader.load(expectedTag.tagId) + + expect(tag).toEqual(expectedTag) + }) + }) + describe('given a list of ids', () => { + it('returns a list of tags', async () => { + const tagIds = [] + const expectedTags = [] + const expectedCursor = await query` + FOR tag IN tags + RETURN { + _type: "tag", + "tagId": tag.tagId, + "label": TRANSLATE('en', tag.label), + "description": TRANSLATE('en', tag.description), + "visible": tag.visible, + } + ` + + while (expectedCursor.hasMore) { + const tempTag = await expectedCursor.next() + tagIds.push(tempTag.tagId) + expectedTags.push(tempTag) + } + + const loader = loadTagByTagId({ query, language: 'en', i18n }) + const tags = await loader.loadMany(tagIds) + expect(tags).toEqual(expectedTags) + }) + }) + }) + describe('given an unsuccessful load', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + describe('database error is raised', () => { + it('returns an error', async () => { + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadTagByTagId({ + query: mockedQuery, + language: 'en', + userKey: '1234', + i18n, + }) + + try { + await loader.load('1') + } catch (err) { + expect(err).toEqual(new Error('Unable to load tag(s). Please try again.')) + } + + expect(consoleOutput).toEqual([ + `Database error occurred when user: 1234 running loadTagByTagId: Error: Database error occurred.`, + ]) + }) + }) + describe('cursor error is raised', () => { + it('returns an error', async () => { + const cursor = { + forEach() { + throw new Error('Cursor error occurred.') + }, + } + const mockedQuery = jest.fn().mockReturnValue(cursor) + const loader = loadTagByTagId({ + query: mockedQuery, + language: 'en', + userKey: '1234', + i18n, + }) + + try { + await loader.load('1') + } catch (err) { + expect(err).toEqual(new Error('Unable to load tag(s). Please try again.')) + } + + expect(consoleOutput).toEqual([ + `Cursor error occurred when user: 1234 during loadTagByTagId: Error: Cursor error occurred.`, + ]) + }) + }) + }) +}) diff --git a/api/src/tags/loaders/__tests__/load-tags-by-org.test.js b/api/src/tags/loaders/__tests__/load-tags-by-org.test.js new file mode 100644 index 000000000..7c2140bf6 --- /dev/null +++ b/api/src/tags/loaders/__tests__/load-tags-by-org.test.js @@ -0,0 +1,221 @@ +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' + +import englishMessages from '../../../locale/en/messages' +import frenchMessages from '../../../locale/fr/messages' +import { loadTagsByOrg } from '../index' +import dbschema from '../../../../database.json' + +const { DB_PASS: rootPass, DB_URL: url } = process.env + +describe('given a loadTagsByOrg dataloader', () => { + let query, drop, truncate, collections, i18n + + const consoleOutput = [] + const mockedError = (output) => consoleOutput.push(output) + beforeAll(() => { + console.error = mockedError + }) + afterEach(() => { + consoleOutput.length = 0 + }) + + describe('given a successful load', () => { + beforeAll(async () => { + ;({ query, drop, truncate, collections } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + }) + beforeEach(async () => { + await collections.tags.save({ + tagId: 'web-web', + label: { en: 'Web', fr: 'Web' }, + description: { en: '', fr: '' }, + visible: false, + ownership: 'global', + }) + await collections.tags.save({ + tagId: 'new-nouveau', + label: { en: 'New', fr: 'Nouveau' }, + description: { en: '', fr: '' }, + visible: true, + ownership: 'pending', + organizations: ['test'], + }) + await collections.tags.save({ + tagId: 'test-test', + label: { en: 'Test', fr: 'Test' }, + description: { en: '', fr: '' }, + visible: true, + ownership: 'org', + organizations: ['test'], + }) + }) + afterEach(async () => { + await truncate() + }) + afterAll(async () => { + await drop() + }) + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + it('returns only org tags', async () => { + // Get User From db + const expectedCursor = await query` + FOR tag IN tags + FILTER tag.visible == true + FILTER 'test' IN tag.organizations + LET label = TRANSLATE('en', tag.label) + SORT label ASC + RETURN { + "tagId": tag.tagId, + "label": label, + "description": TRANSLATE('en', tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + const expectedTags = await expectedCursor.all() + + const loader = loadTagsByOrg({ query, language: 'en', i18n }) + const tags = await loader({ + orgId: 'test', + includeGlobal: false, + includePending: false, + sortDirection: 'ASC', + }) + + expect(tags).toEqual(expectedTags) + }) + it('returns pending tags', async () => { + const expectedCursor = await query` + FOR tag IN tags + FILTER tag.visible == true + FILTER 'test' IN tag.organizations OR tag.ownership == "pending" + LET label = TRANSLATE('en', tag.label) + SORT label ASC + RETURN { + "tagId": tag.tagId, + "label": label, + "description": TRANSLATE('en', tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + const expectedTags = await expectedCursor.all() + + const loader = loadTagsByOrg({ query, language: 'en', i18n }) + const tags = await loader({ orgId: 'test', includePending: true, sortDirection: 'ASC' }) + + expect(tags).toEqual(expectedTags) + }) + it('returns global tags', async () => { + const expectedCursor = await query` + FOR tag IN tags + FILTER tag.visible == true + FILTER 'test' IN tag.organizations OR tag.ownership == "global" + LET label = TRANSLATE('en', tag.label) + SORT label ASC + RETURN { + "tagId": tag.tagId, + "label": label, + "description": TRANSLATE('en', tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + const expectedTags = await expectedCursor.all() + + const loader = loadTagsByOrg({ query, language: 'en', i18n }) + const tags = await loader({ orgId: 'test', includeGlobal: true, sortDirection: 'ASC' }) + + expect(tags).toEqual(expectedTags) + }) + }) + describe('given an unsuccessful load', () => { + beforeAll(() => { + i18n = setupI18n({ + locale: 'en', + localeData: { + en: { plurals: {} }, + fr: { plurals: {} }, + }, + locales: ['en', 'fr'], + messages: { + en: englishMessages.messages, + fr: frenchMessages.messages, + }, + }) + }) + describe('database error is raised', () => { + it('returns an error', async () => { + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadTagsByOrg({ + query: mockedQuery, + language: 'en', + userKey: '1234', + i18n, + }) + + try { + await loader({ isVisible: false }) + } catch (err) { + expect(err).toEqual(new Error('Unable to load tag(s). Please try again.')) + } + + expect(consoleOutput).toEqual([ + `Database error occurred while user: 1234 was trying to query tags in loadTagsByOrg, Error: Database error occurred.`, + ]) + }) + }) + describe('cursor error is raised', () => { + it('returns an error', async () => { + const cursor = { + all() { + throw new Error('Cursor error occurred.') + }, + } + const mockedQuery = jest.fn().mockReturnValue(cursor) + const loader = loadTagsByOrg({ + query: mockedQuery, + language: 'en', + userKey: '1234', + i18n, + }) + + try { + await loader({ isVisible: false }) + } catch (err) { + expect(err).toEqual(new Error('Unable to load tag(s). Please try again.')) + } + + expect(consoleOutput).toEqual([ + `Cursor error occurred while user: 1234 was trying to gather tags in loadTagsByOrg, Error: Cursor error occurred.`, + ]) + }) + }) + }) +}) diff --git a/api/src/tags/loaders/index.js b/api/src/tags/loaders/index.js new file mode 100644 index 000000000..2048f7983 --- /dev/null +++ b/api/src/tags/loaders/index.js @@ -0,0 +1,3 @@ +export * from './load-all-tags' +export * from './load-tag-by-tag-id' +export * from './load-tags-by-org' diff --git a/api/src/tags/loaders/load-all-tags.js b/api/src/tags/loaders/load-all-tags.js new file mode 100644 index 000000000..5b1256add --- /dev/null +++ b/api/src/tags/loaders/load-all-tags.js @@ -0,0 +1,48 @@ +import { aql } from 'arangojs' +import { t } from '@lingui/macro' + +export const loadAllTags = + ({ query, userKey, i18n, language }) => + async ({ isVisible, orgId }) => { + let visibleFilter = aql`` + if (isVisible) { + visibleFilter = aql`FILTER tag.visible == true` + } + + let orgFilter = aql`` + if (orgId) { + orgFilter = aql`FILTER ${orgId} IN tag.organizations` + } + + let cursor + try { + cursor = await query` + FOR tag IN tags + ${visibleFilter} + ${orgFilter} + LET label = TRANSLATE(${language}, tag.label) + SORT label ASC + RETURN { + "tagId": tag.tagId, + "label": label, + "description": TRANSLATE(${language}, tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + } catch (err) { + console.error(`Database error occurred while user: ${userKey} was trying to query tags in loadAllTags, ${err}`) + throw new Error(i18n._(t`Unable to load tag(s). Please try again.`)) + } + + let tagInfo + try { + tagInfo = await cursor.all() + } catch (err) { + console.error(`Cursor error occurred while user: ${userKey} was trying to gather tags in loadAllTags, ${err}`) + throw new Error(i18n._(t`Unable to load tag(s). Please try again.`)) + } + + return tagInfo + } diff --git a/api/src/tags/loaders/load-tag-by-tag-id.js b/api/src/tags/loaders/load-tag-by-tag-id.js new file mode 100644 index 000000000..b25991c7a --- /dev/null +++ b/api/src/tags/loaders/load-tag-by-tag-id.js @@ -0,0 +1,39 @@ +import DataLoader from 'dataloader' +import { t } from '@lingui/macro' + +export const loadTagByTagId = ({ query, userKey, i18n, language }) => + new DataLoader(async (tags) => { + let cursor + + try { + cursor = await query` + WITH tags + FOR tag IN tags + FILTER tag.tagId IN ${tags} + RETURN { + _type: "tag", + "tagId": tag.tagId, + "label": TRANSLATE(${language}, tag.label), + "description": TRANSLATE(${language}, tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + } catch (err) { + console.error(`Database error occurred when user: ${userKey} running loadTagByTagId: ${err}`) + throw new Error(i18n._(t`Unable to load tag(s). Please try again.`)) + } + + const tagMap = {} + try { + await cursor.forEach((tag) => { + tagMap[tag.tagId] = tag + }) + } catch (err) { + console.error(`Cursor error occurred when user: ${userKey} during loadTagByTagId: ${err}`) + throw new Error(i18n._(t`Unable to load tag(s). Please try again.`)) + } + + return tags.map((tag) => tagMap[tag]) + }) diff --git a/api/src/tags/loaders/load-tags-by-org.js b/api/src/tags/loaders/load-tags-by-org.js new file mode 100644 index 000000000..e5f569602 --- /dev/null +++ b/api/src/tags/loaders/load-tags-by-org.js @@ -0,0 +1,48 @@ +import { aql } from 'arangojs' +import { t } from '@lingui/macro' + +export const loadTagsByOrg = + ({ query, userKey, i18n, language }) => + async ({ orgId, includeGlobal, includePending, sortDirection }) => { + let globalFilter = aql`` + if (includeGlobal) { + globalFilter = aql`OR tag.ownership == "global"` + } + + let pendingFilter = aql`` + if (includePending) { + pendingFilter = aql`OR tag.ownership == "pending"` + } + + let cursor + try { + cursor = await query` + FOR tag IN tags + FILTER tag.visible == true + FILTER ${orgId} IN tag.organizations ${globalFilter} ${pendingFilter} + LET label = TRANSLATE(${language}, tag.label) + SORT label ${sortDirection} + RETURN { + "tagId": tag.tagId, + "label": label, + "description": TRANSLATE(${language}, tag.description), + "visible": tag.visible, + "ownership": tag.ownership, + "organizations": tag.organizations, + } + ` + } catch (err) { + console.error(`Database error occurred while user: ${userKey} was trying to query tags in loadTagsByOrg, ${err}`) + throw new Error(i18n._(t`Unable to load tag(s). Please try again.`)) + } + + let tagInfo + try { + tagInfo = await cursor.all() + } catch (err) { + console.error(`Cursor error occurred while user: ${userKey} was trying to gather tags in loadTagsByOrg, ${err}`) + throw new Error(i18n._(t`Unable to load tag(s). Please try again.`)) + } + + return tagInfo + } diff --git a/api/src/tags/mutations/create-tag.js b/api/src/tags/mutations/create-tag.js new file mode 100644 index 000000000..293dbc216 --- /dev/null +++ b/api/src/tags/mutations/create-tag.js @@ -0,0 +1,177 @@ +import { GraphQLNonNull, GraphQLID, GraphQLBoolean, GraphQLString } from 'graphql' +import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' +import { t } from '@lingui/macro' +import { createTagUnion } from '../unions' +import { TagOwnershipEnums } from '../../enums' +import ac from '../../access-control' + +export const createTag = new mutationWithClientMutationId({ + name: 'CreateTag', + description: 'Mutation used to create a new label for tagging domains.', + inputFields: () => ({ + labelEn: { + type: new GraphQLNonNull(GraphQLString), + description: 'English label that will be displayed.', + }, + labelFr: { + description: 'French label that will be displayed.', + type: new GraphQLNonNull(GraphQLString), + }, + descriptionEn: { + description: 'English description of what the tag describes about a domain.', + type: GraphQLString, + }, + descriptionFr: { + description: 'French description of what the tag describes about a domain.', + type: GraphQLString, + }, + isVisible: { + description: 'Value used to decide if users should see the tag.', + type: GraphQLBoolean, + }, + ownership: { + description: 'Ownership of the tag, can be `global`, `org`, or `pending`.', + type: new GraphQLNonNull(TagOwnershipEnums), + }, + orgId: { + description: 'The global id of the organization to be affiliated with the tag.', + type: GraphQLID, + }, + }), + outputFields: () => ({ + result: { + type: createTagUnion, + description: '`CreateTagUnion` returning either a `Tag`, or `TagError` object.', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { + i18n, + request, + userKey, + auth: { userRequired, verifiedRequired, checkPermission, checkSuperAdmin, superAdminRequired }, + loaders: { loadOrgByKey }, + dataSources: { tags, auditLogs }, + validators: { cleanseInput, slugify }, + }, + ) => { + // Get User + const user = await userRequired() + verifiedRequired({ user }) + + // Cleanse input + const labelEn = cleanseInput(args.labelEn).toLowerCase() + const labelFr = cleanseInput(args.labelFr).toLowerCase() + const descriptionEn = cleanseInput(args.descriptionEn) + const descriptionFr = cleanseInput(args.descriptionFr) + const ownership = cleanseInput(args.ownership) + const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) + + const insertTag = { + tagId: slugify(`${labelEn}-${labelFr}`), + label: { en: labelEn, fr: labelFr }, + description: { + en: descriptionEn || '', + fr: descriptionFr || '', + }, + visible: args?.isVisible ?? true, + ownership, + organizations: [], + } + + const tag = await tags.byTagId.load(insertTag.tagId) + + const isSuperAdmin = await checkSuperAdmin() + if (ownership === 'global') { + superAdminRequired({ user, isSuperAdmin }) + if (typeof tag !== 'undefined') { + console.warn(`User: ${userKey} attempted to create a tag that already exists: ${insertTag.tagId}`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Tag label already in use. Please try again with a different label.`), + } + } + } + + let permission, org + if (ownership === 'org') { + if (typeof orgId === 'undefined') { + console.warn( + `User: ${userKey} attempted to create a tag: ${insertTag.tagId}, however organization-owned tags must have a valid organization.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to create tag, tagId already in use.`), + } + } + + // Check to see if org exists + org = await loadOrgByKey.load(orgId) + if (typeof org === 'undefined') { + console.warn(`User: ${userKey} attempted to create a tag to an organization: ${orgId} that does not exist.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to create tag in unknown organization.`), + } + } + + permission = await checkPermission({ orgId: org._id }) + if (!ac.can(permission).createOwn('tag').granted) { + console.warn( + `User: ${userKey} attempted to create a tag in: ${org.slug}, however they do not have permission to do so.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Permission Denied: Please contact organization admin for help with creating tag.`), + } + } + + if (permission !== 'super_admin' && typeof tag === 'undefined') insertTag.ownership = 'pending' + + if (typeof tag === 'undefined') { + insertTag.organizations = [orgId] + } else if (!tag.organizations.includes(orgId)) { + insertTag.organizations = [...tag.organizations, orgId] + } else { + console.warn( + `User: ${userKey} attempted to create a tag in org:${orgId} that already exists: ${insertTag.tagId}`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Tag label already in use. Please try again with a different label.`), + } + } + } + + const returnTag = await tags.create(insertTag) + + console.info(`User: ${userKey} successfully created tag ${returnTag.tagId}`) + + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: isSuperAdmin ? 'super_admin' : permission, + ipAddress: request.ip, + }, + action: 'add', + target: { + resource: insertTag.tagId, // name of resource being acted upon + organization: org && { + id: org._key, + name: org.name, + }, + resourceType: 'tag', + }, + }) + + return returnTag + }, +}) diff --git a/api/src/tags/mutations/index.js b/api/src/tags/mutations/index.js new file mode 100644 index 000000000..09b1b6647 --- /dev/null +++ b/api/src/tags/mutations/index.js @@ -0,0 +1,2 @@ +export * from './create-tag' +export * from './update-tag' diff --git a/api/src/tags/mutations/update-tag.js b/api/src/tags/mutations/update-tag.js new file mode 100644 index 000000000..bf50c1b3b --- /dev/null +++ b/api/src/tags/mutations/update-tag.js @@ -0,0 +1,250 @@ +import { GraphQLNonNull, GraphQLBoolean, GraphQLString, GraphQLID } from 'graphql' +import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' +import { t } from '@lingui/macro' +import { updateTagUnion } from '../unions' +import { TagOwnershipEnums } from '../../enums' +import ac from '../../access-control' + +export const updateTag = new mutationWithClientMutationId({ + name: 'UpdateTag', + description: 'Mutation used to update labels for tagging domains.', + inputFields: () => ({ + tagId: { + type: new GraphQLNonNull(GraphQLString), + description: 'A unique identifier for the tag.', + }, + labelEn: { + type: GraphQLString, + description: 'English label that will be displayed.', + }, + labelFr: { + description: 'French label that will be displayed.', + type: GraphQLString, + }, + descriptionEn: { + description: 'English description of what the tag describes about a domain.', + type: GraphQLString, + }, + descriptionFr: { + description: 'French description of what the tag describes about a domain.', + type: GraphQLString, + }, + isVisible: { + description: 'Value used to decide if users should see the tag.', + type: GraphQLBoolean, + }, + ownership: { + type: TagOwnershipEnums, + description: 'Ownership of the tag, can be `global`, `org`, or `pending`.', + }, + orgId: { + description: 'The global id of the organization to be affiliated with the tag.', + type: GraphQLID, + }, + }), + outputFields: () => ({ + result: { + type: updateTagUnion, + description: '`UpdateTagUnion` returning either a `Tag`, or `TagError` object.', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { + i18n, + request, + userKey, + auth: { userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired, checkPermission }, + validators: { cleanseInput, slugify }, + loaders: { loadOrgByKey }, + dataSources: { tags, auditLogs }, + }, + ) => { + // Get User + const user = await userRequired() + verifiedRequired({ user }) + + // Cleanse input + const tagId = cleanseInput(args.tagId) + const labelEn = cleanseInput(args.labelEn)?.toLowerCase() + const labelFr = cleanseInput(args.labelFr)?.toLowerCase() + const descriptionEn = cleanseInput(args.descriptionEn) + const descriptionFr = cleanseInput(args.descriptionFr) + const ownership = cleanseInput(args.ownership) + const isVisible = args.isVisible + const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) + + const compareTag = await tags.getRaw(tagId) + + if (typeof compareTag === 'undefined') { + console.warn( + `User: ${userKey} attempted to update tag: ${tagId}, however there is no tag associated with that id.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to update unknown tag.`), + } + } + + const isSuperAdmin = await checkSuperAdmin() + if (['global', 'pending'].includes(compareTag.ownership)) superAdminRequired({ user, isSuperAdmin }) + + let permission, org + if (compareTag.ownership === 'org') { + if (typeof orgId === 'undefined') { + console.warn( + `User: ${userKey} attempted to update a tag: ${compareTag.tagId}, however organization-owned tags must have a valid organization.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to update tag, orgId is invalid.`), + } + } + // Check to see if org exists + org = await loadOrgByKey.load(orgId) + if (typeof org === 'undefined') { + console.warn(`User: ${userKey} attempted to update a tag to an organization: ${orgId} that does not exist.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to update tag in unknown organization.`), + } + } + + permission = await checkPermission({ orgId: org._id }) + if (!ac.can(permission).updateOwn('tag').granted) { + console.warn( + `User: ${userKey} attempted to update a tag in: ${org.slug}, however they do not have permission to do so.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Permission Denied: Please contact organization admin for help with updating tag.`), + } + } + + if (!compareTag.organizations.includes(org._key)) { + console.warn( + `User: ${userKey} attempted to update a tag in: ${org.slug}, however the tag does not belong to this org.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Permission Denied: Please contact organization admin for help with updating tag.`), + } + } + + if (compareTag.organizations.length > 1 && !isSuperAdmin) { + console.warn( + `User: ${userKey} attempted to update a tag in: ${org.slug}, however the tag belongs to more than one org.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Permission Denied: Please contact super admin for help with updating tag.`), + } + } + } + + const updatedTagId = slugify(`${labelEn || compareTag.label.en}-${labelFr || compareTag.label.fr}`) + + if (tagId !== updatedTagId) { + const existingTag = await tags.byTagId.load(updatedTagId) + if (typeof existingTag !== 'undefined' && !['org', 'pending'].includes(compareTag.ownership)) { + console.warn(`User: ${userKey} attempted to update a tag that already exists: ${updatedTagId}`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Tag label already in use. Please try again with a different label.`), + } + } + } + + // Update tag + const updatedTag = { + tagId: updatedTagId, + label: { + en: labelEn || compareTag.label.en, + fr: labelFr || compareTag.label.fr, + }, + description: { + en: descriptionEn || compareTag.description.en, + fr: descriptionFr || compareTag.description.fr, + }, + visible: typeof isVisible !== 'undefined' ? isVisible : compareTag.visible, + ownership: ownership || compareTag.ownership, + } + + const returnTag = await tags.save(tagId, updatedTag) + + console.info(`User: ${userKey} successfully updated tag: ${tagId}.`) + + const updatedProperties = [] + if (labelEn) { + updatedProperties.push({ + name: 'labelEn', + oldValue: compareTag.label.en, + newValue: labelEn, + }) + } + if (labelFr) { + updatedProperties.push({ + name: 'labelFr', + oldValue: compareTag.label.fr, + newValue: labelFr, + }) + } + if (descriptionEn) { + updatedProperties.push({ + name: 'descriptionEn', + oldValue: compareTag.description.en, + newValue: descriptionEn, + }) + } + if (descriptionFr) { + updatedProperties.push({ + name: 'descriptionFr', + oldValue: compareTag.description.fr, + newValue: descriptionFr, + }) + } + if (typeof isVisible !== 'undefined') { + updatedProperties.push({ + name: 'visible', + oldValue: compareTag.visible, + newValue: isVisible, + }) + } + if (typeof ownership !== 'undefined') { + updatedProperties.push({ + name: 'ownership', + oldValue: compareTag.ownership, + newValue: ownership, + }) + } + + await auditLogs.logActivity({ + initiatedBy: { + id: user._key, + userName: user.userName, + role: isSuperAdmin ? 'super_admin' : permission, + ipAddress: request.ip, + }, + action: 'update', + target: { + resource: updatedTag.tagId, + updatedProperties, + organization: org && { + id: org._key, + name: org.name, + }, // name of resource being acted upon + resourceType: 'tag', // user, org, domain + }, + }) + + return returnTag + }, +}) diff --git a/api/src/tags/objects/index.js b/api/src/tags/objects/index.js new file mode 100644 index 000000000..f9414f2e0 --- /dev/null +++ b/api/src/tags/objects/index.js @@ -0,0 +1,2 @@ +export * from './tag' +export * from './tag-error' diff --git a/api/src/tags/objects/tag-error.js b/api/src/tags/objects/tag-error.js new file mode 100644 index 000000000..39f94099a --- /dev/null +++ b/api/src/tags/objects/tag-error.js @@ -0,0 +1,18 @@ +import { GraphQLInt, GraphQLObjectType, GraphQLString } from 'graphql' + +export const tagErrorType = new GraphQLObjectType({ + name: 'TagError', + description: 'This object is used to inform the user if any errors occurred while using a tag mutation.', + fields: () => ({ + code: { + type: GraphQLInt, + description: 'Error code to inform user what the issue is related to.', + resolve: ({ code }) => code, + }, + description: { + type: GraphQLString, + description: 'Description of the issue that was encountered.', + resolve: ({ description }) => description, + }, + }), +}) diff --git a/api/src/tags/objects/tag.js b/api/src/tags/objects/tag.js new file mode 100644 index 000000000..6cfa61fe1 --- /dev/null +++ b/api/src/tags/objects/tag.js @@ -0,0 +1,66 @@ +import { GraphQLBoolean, GraphQLList, GraphQLObjectType, GraphQLString } from 'graphql' +import { TagOwnershipEnums } from '../../enums/tag-ownership' +import { organizationType } from '../../organization/objects' + +export const tagType = new GraphQLObjectType({ + name: 'Tag', + fields: () => ({ + tagId: { + type: GraphQLString, + description: 'A unique identifier for the tag.', + resolve: ({ tagId }) => tagId, + }, + label: { + type: GraphQLString, + description: 'The display name or label of the tag.', + resolve: ({ label }) => label, + }, + description: { + type: GraphQLString, + description: 'A brief description of the tag.', + resolve: ({ description }) => description, + }, + isVisible: { + type: GraphQLBoolean, + description: 'Indicates whether the tag is visible to users.', + resolve: ({ visible }) => visible, + }, + ownership: { + type: TagOwnershipEnums, + description: '', + resolve: ({ ownership }) => ownership, + }, + organizations: { + type: new GraphQLList(organizationType), + description: '', + resolve: async ( + { tagId, organizations, ownership }, + _, + { + userKey, + auth: { userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired }, + loaders: { loadOrgByKey }, + }, + ) => { + const user = await userRequired() + verifiedRequired({ user }) + + const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ user, isSuperAdmin }) + + if (ownership === 'global') return [] + + const orgs = [] + for (const orgId of organizations) { + const org = await loadOrgByKey.load(orgId) + if (!org) continue + orgs.push(org) + } + + console.info(`User: ${userKey} successfully retrieved affiliated orgs for tag: ${tagId}.`) + + return orgs + }, + }, + }), +}) diff --git a/api/src/tags/queries/__tests__/find-all-tags.test.js b/api/src/tags/queries/__tests__/find-all-tags.test.js new file mode 100644 index 000000000..1673b6cba --- /dev/null +++ b/api/src/tags/queries/__tests__/find-all-tags.test.js @@ -0,0 +1,69 @@ +import { findAllTags } from '../find-all-tags' + +describe('findAllTags', () => { + let tagsAll, userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired, userKey, context, cleanseInput + + beforeEach(() => { + tagsAll = jest.fn() + userRequired = jest.fn() + verifiedRequired = jest.fn() + checkSuperAdmin = jest.fn() + superAdminRequired = jest.fn() + cleanseInput = jest.fn() + + userKey = 'test-user' + context = { + userKey, + dataSources: { tags: { all: tagsAll } }, + loaders: { loadOrgByKey: jest.fn() }, + auth: { userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired }, + validators: { cleanseInput }, + } + }) + + it('should return tags when loadAllTags is successful', async () => { + const tags = [ + { tagId: '1', label: 'Tag1', description: 'Description1', visible: true, ownership: 'global', organizations: [] }, + { + tagId: '2', + label: 'Tag2', + description: 'Description2', + visible: false, + ownership: 'global', + organizations: [], + }, + ] + tagsAll.mockResolvedValue(tags) + + const result = await findAllTags.resolve(null, { isVisible: false }, context) + + expect(tagsAll).toHaveBeenCalledWith({ isVisible: false, orgId: null }) + expect(result).toEqual(tags) + }) + + it('should apply visible filter when isVisible is true', async () => { + const tags = [{ tagId: '1', label: 'Tag1', description: 'Description1', visible: true, ownership: 'global' }] + tagsAll.mockResolvedValue(tags) + + const result = await findAllTags.resolve(null, { isVisible: true }, context) + + expect(tagsAll).toHaveBeenCalledWith({ isVisible: true, orgId: null }) + expect(result).toEqual(tags) + }) + + it('should log a message when tags are successfully retrieved', async () => { + const tags = [{ tagId: '1', label: 'Tag1', description: 'Description1', visible: true, ownership: 'global' }] + tagsAll.mockResolvedValue(tags) + console.info = jest.fn() + + await findAllTags.resolve(null, { isVisible: false }, context) + + expect(console.info).toHaveBeenCalledWith(`User: ${userKey} successfully retrieved tags.`) + }) + + it('should throw an error when loadAllTags fails', async () => { + tagsAll.mockRejectedValue(new Error('Load error')) + + await expect(findAllTags.resolve(null, { isVisible: false }, context)).rejects.toThrow('Load error') + }) +}) diff --git a/api/src/tags/queries/find-all-tags.js b/api/src/tags/queries/find-all-tags.js new file mode 100644 index 000000000..31b58c411 --- /dev/null +++ b/api/src/tags/queries/find-all-tags.js @@ -0,0 +1,47 @@ +import { GraphQLBoolean, GraphQLList, GraphQLID } from 'graphql' +import { tagType } from '../objects' +import { fromGlobalId } from 'graphql-relay' + +export const findAllTags = { + type: new GraphQLList(tagType), + description: 'All dynamically generated tags users have access to.', + args: { + orgId: { + type: GraphQLID, + description: 'The organization you wish to query the tags from.', + }, + isVisible: { + type: GraphQLBoolean, + description: 'Indicates whether the tag is visible to users.', + }, + }, + resolve: async ( + _, + args, + { + userKey, + auth: { userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired }, + loaders: { loadOrgByKey }, + dataSources: { tags: tagsSource }, + validators: { cleanseInput }, + }, + ) => { + const user = await userRequired() + verifiedRequired({ user }) + + let orgKey = null + if (args.orgId) { + const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) + // Get Org from db + const org = await loadOrgByKey.load(orgId) + orgKey = org?._key + } else { + const isSuperAdmin = await checkSuperAdmin() + superAdminRequired({ user, isSuperAdmin }) + } + + const tags = await tagsSource.all({ ...args, orgId: orgKey }) + console.info(`User: ${userKey} successfully retrieved tags.`) + return tags + }, +} diff --git a/api/src/tags/queries/index.js b/api/src/tags/queries/index.js new file mode 100644 index 000000000..59cb6ecec --- /dev/null +++ b/api/src/tags/queries/index.js @@ -0,0 +1 @@ +export * from './find-all-tags' diff --git a/api/src/tags/unions/__tests__/create-tag-union.test.js b/api/src/tags/unions/__tests__/create-tag-union.test.js new file mode 100644 index 000000000..75e4b1f3c --- /dev/null +++ b/api/src/tags/unions/__tests__/create-tag-union.test.js @@ -0,0 +1,36 @@ +import { createTagUnion } from '../create-tag-union' +import { tagErrorType, tagType } from '../../objects' +import { GraphQLUnionType } from 'graphql' + +describe('createTagUnion', () => { + it('should be an instance of GraphQLUnionType', () => { + expect(createTagUnion).toBeInstanceOf(GraphQLUnionType) + }) + + it('should have the correct name', () => { + expect(createTagUnion.name).toBe('CreateTagUnion') + }) + + it('should have the correct description', () => { + expect(createTagUnion.description).toBe(`This union is used with the \`CreateTag\` mutation, +allowing for users to create a tag and add it to their org, +and support any errors that may occur`) + }) + + it('should have the correct types', () => { + expect(createTagUnion.getTypes()).toContain(tagErrorType) + expect(createTagUnion.getTypes()).toContain(tagType) + }) + + describe('resolveType', () => { + it('should return tagType name when _type is "tag"', () => { + const result = createTagUnion.resolveType({ _type: 'tag' }) + expect(result).toBe(tagType.name) + }) + + it('should return tagErrorType name when _type is not "tag"', () => { + const result = createTagUnion.resolveType({ _type: 'error' }) + expect(result).toBe(tagErrorType.name) + }) + }) +}) diff --git a/api/src/tags/unions/__tests__/update-tag-union.test.js b/api/src/tags/unions/__tests__/update-tag-union.test.js new file mode 100644 index 000000000..1d6a640e3 --- /dev/null +++ b/api/src/tags/unions/__tests__/update-tag-union.test.js @@ -0,0 +1,36 @@ +import { updateTagUnion } from '../update-tag-union' +import { tagErrorType, tagType } from '../../objects' +import { GraphQLUnionType } from 'graphql' + +describe('updateTagUnion', () => { + it('should be an instance of GraphQLUnionType', () => { + expect(updateTagUnion).toBeInstanceOf(GraphQLUnionType) + }) + + it('should have the correct name', () => { + expect(updateTagUnion.name).toBe('UpdateTagUnion') + }) + + it('should have the correct description', () => { + expect(updateTagUnion.description).toBe(`This union is used with the \`UpdateTag\` mutation, +allowing for users to update a tag and add it to their org, +and support any errors that may occur`) + }) + + it('should have the correct types', () => { + expect(updateTagUnion.getTypes()).toContain(tagErrorType) + expect(updateTagUnion.getTypes()).toContain(tagType) + }) + + describe('resolveType', () => { + it('should return tagType name when _type is "tag"', () => { + const result = updateTagUnion.resolveType({ _type: 'tag' }) + expect(result).toBe(tagType.name) + }) + + it('should return tagErrorType name when _type is not "tag"', () => { + const result = updateTagUnion.resolveType({ _type: 'error' }) + expect(result).toBe(tagErrorType.name) + }) + }) +}) diff --git a/api/src/tags/unions/create-tag-union.js b/api/src/tags/unions/create-tag-union.js new file mode 100644 index 000000000..5eccb6cc6 --- /dev/null +++ b/api/src/tags/unions/create-tag-union.js @@ -0,0 +1,17 @@ +import { GraphQLUnionType } from 'graphql' +import { tagErrorType, tagType } from '../objects' + +export const createTagUnion = new GraphQLUnionType({ + name: 'CreateTagUnion', + description: `This union is used with the \`CreateTag\` mutation, +allowing for users to create a tag and add it to their org, +and support any errors that may occur`, + types: [tagErrorType, tagType], + resolveType({ _type }) { + if (_type === 'tag') { + return tagType.name + } else { + return tagErrorType.name + } + }, +}) diff --git a/api/src/tags/unions/index.js b/api/src/tags/unions/index.js new file mode 100644 index 000000000..9c4ea619c --- /dev/null +++ b/api/src/tags/unions/index.js @@ -0,0 +1,2 @@ +export * from '../unions/create-tag-union' +export * from './update-tag-union' diff --git a/api/src/tags/unions/update-tag-union.js b/api/src/tags/unions/update-tag-union.js new file mode 100644 index 000000000..0aaca32ee --- /dev/null +++ b/api/src/tags/unions/update-tag-union.js @@ -0,0 +1,17 @@ +import { GraphQLUnionType } from 'graphql' +import { tagErrorType, tagType } from '../objects' + +export const updateTagUnion = new GraphQLUnionType({ + name: 'UpdateTagUnion', + description: `This union is used with the \`UpdateTag\` mutation, +allowing for users to update a tag and add it to their org, +and support any errors that may occur`, + types: [tagErrorType, tagType], + resolveType({ _type }) { + if (_type === 'tag') { + return tagType.name + } else { + return tagErrorType.name + } + }, +}) diff --git a/api/src/testUtilities.js b/api/src/testUtilities.js new file mode 100644 index 000000000..7df1884dc --- /dev/null +++ b/api/src/testUtilities.js @@ -0,0 +1,63 @@ +import { ensure } from 'arango-tools' +import { Database } from 'arangojs' +import { tokenize } from './auth' +import { createContext } from './create-context' + +export async function ensureDatabase(options) { + let variables + if (options.variables) { + variables = options.variables + variables.name = variables.dbname + } else { + variables = { ...options } + } + const systemDatabase = new Database({ url: variables.url, databaseName: '_system' }) + await systemDatabase.login('root', variables.rootPassword) + const databases = await systemDatabase.listDatabases() + if (!databases.includes(variables.name)) { + try { + await systemDatabase.createDatabase(variables.name) + } catch (e) { + console.error(`Failed to create database ${variables.name}: ${e.message}`) + process.exit(1) + } + } + + let ensureOptions + if (options.variables) { + ensureOptions = { + variables: options.variables, + schema: { ...options.schema }, + } + } else { + ensureOptions = options + } + + const ensured = await ensure(ensureOptions) + + const db = new Database({ url: variables.url, databaseName: variables.name }) + await db.login(variables.username || 'root', variables.password || variables.rootPassword) + + return { ...ensured, db } +} + +export function createUserContextGenerator({ query, db, transaction, collectionNames, i18n, secret, salt }) { + return async function createUserContext({ userKey, expiry = '60m', language = 'en', loginRequiredBool = true }) { + const signedToken = tokenize({ + expiresIn: expiry, + parameters: { userKey: userKey }, + secret: secret, + }) + return await createContext({ + query, + db, + transaction, + collections: collectionNames, + req: { headers: { authorization: signedToken } }, + i18n, + language: language, + loginRequiredBool: loginRequiredBool, + salt: salt, + }) + } +} diff --git a/api/src/user/index.js b/api/src/user/index.js index 1b5bd9dcd..ee648b991 100644 --- a/api/src/user/index.js +++ b/api/src/user/index.js @@ -3,3 +3,4 @@ export * from './mutations' export * from './objects' export * from './queries' export * from './unions' +export * from './inputs' diff --git a/api/src/user/inputs/__tests__/user-order.test.js b/api/src/user/inputs/__tests__/user-order.test.js new file mode 100644 index 000000000..0bac20dec --- /dev/null +++ b/api/src/user/inputs/__tests__/user-order.test.js @@ -0,0 +1,21 @@ +import { GraphQLNonNull } from 'graphql' + +import { userOrder } from '../user-order' +import { OrderDirection, UserOrderField } from '../../../enums' + +describe('given the affiliationOrder input object', () => { + describe('testing fields', () => { + it('has a direction field', () => { + const demoType = userOrder.getFields() + + expect(demoType).toHaveProperty('direction') + expect(demoType.direction.type).toMatchObject(new GraphQLNonNull(OrderDirection)) + }) + it('has a field field', () => { + const demoType = userOrder.getFields() + + expect(demoType).toHaveProperty('field') + expect(demoType.field.type).toMatchObject(new GraphQLNonNull(UserOrderField)) + }) + }) +}) diff --git a/api/src/user/inputs/email-update-options.js b/api/src/user/inputs/email-update-options.js new file mode 100644 index 000000000..6617b09ec --- /dev/null +++ b/api/src/user/inputs/email-update-options.js @@ -0,0 +1,22 @@ +import { GraphQLBoolean, GraphQLInputObjectType } from 'graphql' + +export const emailUpdatesInput = new GraphQLInputObjectType({ + name: 'emailUpdatesInput', + fields: () => ({ + orgFootprint: { + type: GraphQLBoolean, + description: + "Value used to determine if user wants to receive possibly daily email updates about their organization's digital footprint.", + }, + progressReport: { + type: GraphQLBoolean, + description: + "Value used to determine if user wants to receive monthly email updates about their organization's compliance score progress.", + }, + detectDecay: { + type: GraphQLBoolean, + description: + "Value used to determine if user wants to receive possibly daily email updates about their organization's compliance statuses.", + }, + }), +}) diff --git a/api/src/user/inputs/index.js b/api/src/user/inputs/index.js new file mode 100644 index 000000000..7bcd1778a --- /dev/null +++ b/api/src/user/inputs/index.js @@ -0,0 +1,2 @@ +export * from './user-order' +export * from './email-update-options.js' diff --git a/api/src/user/inputs/user-order.js b/api/src/user/inputs/user-order.js new file mode 100644 index 000000000..ea691c7c6 --- /dev/null +++ b/api/src/user/inputs/user-order.js @@ -0,0 +1,18 @@ +import { GraphQLInputObjectType, GraphQLNonNull } from 'graphql' + +import { OrderDirection, UserOrderField } from '../../enums' + +export const userOrder = new GraphQLInputObjectType({ + name: 'UserOrder', + description: 'Ordering options for affiliation connections.', + fields: () => ({ + field: { + type: new GraphQLNonNull(UserOrderField), + description: 'The field to order affiliations by.', + }, + direction: { + type: new GraphQLNonNull(OrderDirection), + description: 'The ordering direction.', + }, + }), +}) diff --git a/api/src/user/loaders/__tests__/load-user-by-key.test.js b/api/src/user/loaders/__tests__/load-user-by-key.test.js index 5a91f7aa5..ee7f16473 100644 --- a/api/src/user/loaders/__tests__/load-user-by-key.test.js +++ b/api/src/user/loaders/__tests__/load-user-by-key.test.js @@ -1,12 +1,13 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' -import {loadUserByKey} from '../index' +import { loadUserByKey } from '../index' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadUserByKey dataloader', () => { let query, drop, truncate, collections, i18n @@ -18,8 +19,8 @@ describe('given a loadUserByKey dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -34,7 +35,7 @@ describe('given a loadUserByKey dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -50,14 +51,12 @@ describe('given a loadUserByKey dataloader', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) await collections.users.save({ userName: 'random@email.ca', displayName: 'Random Name', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }) @@ -78,7 +77,7 @@ describe('given a loadUserByKey dataloader', () => { ` const expectedUser = await expectedCursor.next() - const loader = loadUserByKey({query}) + const loader = loadUserByKey({ query }) const user = await loader.load(expectedUser._key) expect(user).toEqual(expectedUser) @@ -99,7 +98,7 @@ describe('given a loadUserByKey dataloader', () => { expectedUsers.push(tempUser) } - const loader = loadUserByKey({query}) + const loader = loadUserByKey({ query }) const users = await loader.loadMany(userKeys) expect(users).toEqual(expectedUsers) }) @@ -111,8 +110,8 @@ describe('given a loadUserByKey dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -123,9 +122,7 @@ describe('given a loadUserByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadUserByKey({ query: mockedQuery, userKey: '1234', @@ -135,9 +132,7 @@ describe('given a loadUserByKey dataloader', () => { try { await loader.load('1234') } catch (err) { - expect(err).toEqual( - new Error('Unable to load user(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load user(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -162,9 +157,7 @@ describe('given a loadUserByKey dataloader', () => { try { await loader.load('1234') } catch (err) { - expect(err).toEqual( - new Error('Unable to load user(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load user(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -178,8 +171,8 @@ describe('given a loadUserByKey dataloader', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -190,9 +183,7 @@ describe('given a loadUserByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadUserByKey({ query: mockedQuery, userKey: '1234', @@ -202,11 +193,7 @@ describe('given a loadUserByKey dataloader', () => { try { await loader.load('1234') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) utilisateur(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) utilisateur(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -231,11 +218,7 @@ describe('given a loadUserByKey dataloader', () => { try { await loader.load('1234') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) utilisateur(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) utilisateur(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/user/loaders/__tests__/load-user-by-username.test.js b/api/src/user/loaders/__tests__/load-user-by-username.test.js index 6dd21b4f1..e08aa6280 100644 --- a/api/src/user/loaders/__tests__/load-user-by-username.test.js +++ b/api/src/user/loaders/__tests__/load-user-by-username.test.js @@ -1,12 +1,13 @@ -import {ensure, dbNameFromFile} from 'arango-tools' -import {setupI18n} from '@lingui/core' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' +import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' -import {loadUserByUserName} from '../index' +import { loadUserByUserName } from '../index' import dbschema from '../../../../database.json' -const {DB_PASS: rootPass, DB_URL: url} = process.env +const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadUserByUserName dataloader', () => { let query, drop, truncate, collections, i18n @@ -17,8 +18,8 @@ describe('given a loadUserByUserName dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -33,7 +34,7 @@ describe('given a loadUserByUserName dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({query, drop, truncate, collections} = await ensure({ + ;({ query, drop, truncate, collections } = await ensure({ variables: { dbname: dbNameFromFile(__filename), username: 'root', @@ -49,14 +50,12 @@ describe('given a loadUserByUserName dataloader', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) await collections.users.save({ userName: 'random@email.ca', displayName: 'Random Name', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }) @@ -70,7 +69,7 @@ describe('given a loadUserByUserName dataloader', () => { describe('provided a single username', () => { it('returns a single user', async () => { const userName = 'random@email.ca' - const loader = loadUserByUserName({query, i18n}) + const loader = loadUserByUserName({ query, i18n }) // Get Query User const cursor = await query` @@ -87,11 +86,8 @@ describe('given a loadUserByUserName dataloader', () => { describe('provided a list of usernames', () => { it('returns a list of users', async () => { const expectedUsers = [] - const userNames = [ - 'random@email.ca', - 'test.account@istio.actually.exists', - ] - const loader = loadUserByUserName({query, i18n}) + const userNames = ['random@email.ca', 'test.account@istio.actually.exists'] + const loader = loadUserByUserName({ query, i18n }) for (const i in userNames) { // Get Query User @@ -114,8 +110,8 @@ describe('given a loadUserByUserName dataloader', () => { i18n = setupI18n({ locale: 'en', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -128,17 +124,13 @@ describe('given a loadUserByUserName dataloader', () => { it('throws an error', async () => { const userName = 'random@email.ca' - query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) - const loader = loadUserByUserName({query, userKey: '1234', i18n}) + query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadUserByUserName({ query, userKey: '1234', i18n }) try { await loader.load(userName) } catch (err) { - expect(err).toEqual( - new Error('Unable to load user(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load user(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -156,14 +148,12 @@ describe('given a loadUserByUserName dataloader', () => { }, } query = jest.fn().mockReturnValue(cursor) - const loader = loadUserByUserName({query, userKey: '1234', i18n}) + const loader = loadUserByUserName({ query, userKey: '1234', i18n }) try { await loader.load(userName) } catch (err) { - expect(err).toEqual( - new Error('Unable to load user(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load user(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -177,8 +167,8 @@ describe('given a loadUserByUserName dataloader', () => { i18n = setupI18n({ locale: 'fr', localeData: { - en: {plurals: {}}, - fr: {plurals: {}}, + en: { plurals: {} }, + fr: { plurals: {} }, }, locales: ['en', 'fr'], messages: { @@ -191,19 +181,13 @@ describe('given a loadUserByUserName dataloader', () => { it('throws an error', async () => { const userName = 'random@email.ca' - query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) - const loader = loadUserByUserName({query, userKey: '1234', i18n}) + query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) + const loader = loadUserByUserName({ query, userKey: '1234', i18n }) try { await loader.load(userName) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) utilisateur(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) utilisateur(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -221,16 +205,12 @@ describe('given a loadUserByUserName dataloader', () => { }, } query = jest.fn().mockReturnValue(cursor) - const loader = loadUserByUserName({query, userKey: '1234', i18n}) + const loader = loadUserByUserName({ query, userKey: '1234', i18n }) try { await loader.load(userName) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) utilisateur(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) utilisateur(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/user/loaders/load-my-tracker-by-user-id.js b/api/src/user/loaders/load-my-tracker-by-user-id.js index 4c515fa82..343323e5e 100644 --- a/api/src/user/loaders/load-my-tracker-by-user-id.js +++ b/api/src/user/loaders/load-my-tracker-by-user-id.js @@ -45,7 +45,6 @@ export const loadMyTrackerByUserId = total: 0, }, dmarc_phase: { - not_implemented: 0, assess: 0, deploy: 0, enforce: 0, @@ -70,8 +69,7 @@ export const loadMyTrackerByUserId = returnSummaries.dmarc.total++ // calculate dmarcPhase summary - if (phase === 'not implemented') returnSummaries.dmarc_phase.not_implemented++ - else if (phase === 'assess') returnSummaries.dmarc_phase.assess++ + if (phase === 'assess') returnSummaries.dmarc_phase.assess++ else if (phase === 'deploy') returnSummaries.dmarc_phase.deploy++ else if (phase === 'enforce') returnSummaries.dmarc_phase.enforce++ else if (phase === 'maintain') returnSummaries.dmarc_phase.maintain++ diff --git a/api/src/user/loaders/load-user-connections-by-user-id.js b/api/src/user/loaders/load-user-connections-by-user-id.js index 4d0aeeb6e..14d0a1075 100644 --- a/api/src/user/loaders/load-user-connections-by-user-id.js +++ b/api/src/user/loaders/load-user-connections-by-user-id.js @@ -1,245 +1,235 @@ -import {aql} from 'arangojs' -import {fromGlobalId, toGlobalId} from 'graphql-relay' -import {t} from '@lingui/macro' +import { aql } from 'arangojs' +import { fromGlobalId, toGlobalId } from 'graphql-relay' +import { t } from '@lingui/macro' export const loadUserConnectionsByUserId = - ({query, userKey, cleanseInput, i18n}) => - async ({after, before, first, last, orderBy, isSuperAdmin, search}) => { - const userDBId = `users/${userKey}` + ({ query, userKey, cleanseInput, i18n }) => + async ({ after, before, first, last, orderBy, isSuperAdmin, search }) => { + const userDBId = `users/${userKey}` - let afterTemplate = aql`` - let afterVar = aql`` + let afterTemplate = aql`` + let afterVar = aql`` - if (typeof after !== 'undefined') { - const {id: afterId} = fromGlobalId(cleanseInput(after)) - if (typeof orderBy === 'undefined') { - afterTemplate = aql`FILTER TO_NUMBER(user._key) > TO_NUMBER(${afterId})` + if (typeof after !== 'undefined') { + const { id: afterId } = fromGlobalId(cleanseInput(after)) + if (typeof orderBy === 'undefined') { + afterTemplate = aql`FILTER TO_NUMBER(user._key) > TO_NUMBER(${afterId})` + } else { + let afterTemplateDirection = aql`` + if (orderBy.direction === 'ASC') { + afterTemplateDirection = aql`>` } else { - let afterTemplateDirection = aql`` - if (orderBy.direction === 'ASC') { - afterTemplateDirection = aql`>` - } else { - afterTemplateDirection = aql`<` - } + afterTemplateDirection = aql`<` + } - afterVar = aql`LET afterVar = DOCUMENT(users, ${afterId})` + afterVar = aql`LET afterVar = DOCUMENT(users, ${afterId})` - let documentField = aql`` - let userField = aql`` - /* istanbul ignore else */ - if (orderBy.field === 'user-username') { - documentField = aql`afterVar.userName` - userField = aql`user.userName` - } else if (orderBy.field === 'user-displayName') { - documentField = aql`afterVar.displayName` - userField = aql`user.displayName` - } else if (orderBy.field === 'user-emailValidated') { - documentField = aql`afterVar.emailValidated` - userField = aql`user.emailValidated` - } else if (orderBy.field === 'user-affiliations-totalCount') { - documentField = aql`afterVar.affiliations.totalCount` - userField = aql`user.affiliations.totalCount` - } else if (orderBy.field === 'user-insider') { + let documentField = aql`` + let userField = aql`` + /* istanbul ignore else */ + if (orderBy.field === 'user-username') { + documentField = aql`afterVar.userName` + userField = aql`user.userName` + } else if (orderBy.field === 'user-displayName') { + documentField = aql`afterVar.displayName` + userField = aql`user.displayName` + } else if (orderBy.field === 'user-emailValidated') { + documentField = aql`afterVar.emailValidated` + userField = aql`user.emailValidated` + } else if (orderBy.field === 'user-affiliations-totalCount') { + documentField = aql`afterVar.affiliations.totalCount` + userField = aql`user.affiliations.totalCount` + } else if (orderBy.field === 'user-insider') { documentField = aql`afterVar.insiderUser` userField = aql`user.insiderUser` } - afterTemplate = aql` + afterTemplate = aql` FILTER ${userField} ${afterTemplateDirection} ${documentField} OR (${userField} == ${documentField} AND TO_NUMBER(user._key) > TO_NUMBER(${afterId})) ` - } } + } - let beforeTemplate = aql`` - let beforeVar = aql`` + let beforeTemplate = aql`` + let beforeVar = aql`` - if (typeof before !== 'undefined') { - const {id: beforeId} = fromGlobalId(cleanseInput(before)) - if (typeof orderBy === 'undefined') { - beforeTemplate = aql`FILTER TO_NUMBER(user._key) < TO_NUMBER(${beforeId})` + if (typeof before !== 'undefined') { + const { id: beforeId } = fromGlobalId(cleanseInput(before)) + if (typeof orderBy === 'undefined') { + beforeTemplate = aql`FILTER TO_NUMBER(user._key) < TO_NUMBER(${beforeId})` + } else { + let beforeTemplateDirection = aql`` + if (orderBy.direction === 'ASC') { + beforeTemplateDirection = aql`<` } else { - let beforeTemplateDirection = aql`` - if (orderBy.direction === 'ASC') { - beforeTemplateDirection = aql`<` - } else { - beforeTemplateDirection = aql`>` - } + beforeTemplateDirection = aql`>` + } - beforeVar = aql`LET beforeVar = DOCUMENT(users, ${beforeId})` + beforeVar = aql`LET beforeVar = DOCUMENT(users, ${beforeId})` - let documentField = aql`` - let userField = aql`` - /* istanbul ignore else */ - if (orderBy.field === 'user-username') { - documentField = aql`beforeVar.userName` - userField = aql`user.userName` - } else if (orderBy.field === 'user-displayName') { - documentField = aql`beforeVar.displayName` - userField = aql`user.displayName` - } else if (orderBy.field === 'user-emailValidated') { - documentField = aql`beforeVar.emailValidated` - userField = aql`user.emailValidated` - } else if (orderBy.field === 'user-affiliations-totalCount') { - documentField = aql`beforeVar.affiliations.totalCount` - userField = aql`user.affiliations.totalCount` - } else if (orderBy.field === 'user-insider') { + let documentField = aql`` + let userField = aql`` + /* istanbul ignore else */ + if (orderBy.field === 'user-username') { + documentField = aql`beforeVar.userName` + userField = aql`user.userName` + } else if (orderBy.field === 'user-displayName') { + documentField = aql`beforeVar.displayName` + userField = aql`user.displayName` + } else if (orderBy.field === 'user-emailValidated') { + documentField = aql`beforeVar.emailValidated` + userField = aql`user.emailValidated` + } else if (orderBy.field === 'user-affiliations-totalCount') { + documentField = aql`beforeVar.affiliations.totalCount` + userField = aql`user.affiliations.totalCount` + } else if (orderBy.field === 'user-insider') { documentField = aql`beforeVar.insideUser` userField = aql`user.insideUser` } - beforeTemplate = aql` + beforeTemplate = aql` FILTER ${userField} ${beforeTemplateDirection} ${documentField} OR (${userField} == ${documentField} AND TO_NUMBER(user._key) < TO_NUMBER(${beforeId})) ` - } } + } - let limitTemplate = aql`` - if (typeof first === 'undefined' && typeof last === 'undefined') { + let limitTemplate = aql`` + if (typeof first === 'undefined' && typeof last === 'undefined') { + console.warn( + `User: ${userKey} did not have either \`first\` or \`last\` arguments set for: loadUserConnectionsByUserId.`, + ) + throw new Error( + i18n._(t`You must provide a \`first\` or \`last\` value to properly paginate the \`User\` connection.`), + ) + } else if (typeof first !== 'undefined' && typeof last !== 'undefined') { + console.warn( + `User: ${userKey} attempted to have \`first\` and \`last\` arguments set for: loadUserConnectionsByUserId.`, + ) + throw new Error( + i18n._(t`Passing both \`first\` and \`last\` to paginate the \`User\` connection is not supported.`), + ) + } else if (typeof first === 'number' || typeof last === 'number') { + /* istanbul ignore else */ + if (first < 0 || last < 0) { + const argSet = typeof first !== 'undefined' ? 'first' : 'last' console.warn( - `User: ${userKey} did not have either \`first\` or \`last\` arguments set for: loadUserConnectionsByUserId.`, + `User: ${userKey} attempted to have \`${argSet}\` set below zero for: loadUserConnectionsByUserId.`, ) - throw new Error( - i18n._( - t`You must provide a \`first\` or \`last\` value to properly paginate the \`User\` connection.`, - ), - ) - } else if (typeof first !== 'undefined' && typeof last !== 'undefined') { + throw new Error(i18n._(t`\`${argSet}\` on the \`User\` connection cannot be less than zero.`)) + } else if (first > 100 || last > 100) { + const argSet = typeof first !== 'undefined' ? 'first' : 'last' + const amount = typeof first !== 'undefined' ? first : last console.warn( - `User: ${userKey} attempted to have \`first\` and \`last\` arguments set for: loadUserConnectionsByUserId.`, + `User: ${userKey} attempted to have \`${argSet}\` set to ${amount} for: loadUserConnectionsByUserId.`, ) throw new Error( i18n._( - t`Passing both \`first\` and \`last\` to paginate the \`User\` connection is not supported.`, + t`Requesting \`${amount}\` records on the \`User\` connection exceeds the \`${argSet}\` limit of 100 records.`, ), ) - } else if (typeof first === 'number' || typeof last === 'number') { - /* istanbul ignore else */ - if (first < 0 || last < 0) { - const argSet = typeof first !== 'undefined' ? 'first' : 'last' - console.warn( - `User: ${userKey} attempted to have \`${argSet}\` set below zero for: loadUserConnectionsByUserId.`, - ) - throw new Error( - i18n._( - t`\`${argSet}\` on the \`User\` connection cannot be less than zero.`, - ), - ) - } else if (first > 100 || last > 100) { - const argSet = typeof first !== 'undefined' ? 'first' : 'last' - const amount = typeof first !== 'undefined' ? first : last - console.warn( - `User: ${userKey} attempted to have \`${argSet}\` set to ${amount} for: loadUserConnectionsByUserId.`, - ) - throw new Error( - i18n._( - t`Requesting \`${amount}\` records on the \`User\` connection exceeds the \`${argSet}\` limit of 100 records.`, - ), - ) - } else if (typeof first !== 'undefined' && typeof last === 'undefined') { - limitTemplate = aql`TO_NUMBER(user._key) ASC LIMIT TO_NUMBER(${first})` - } else if (typeof first === 'undefined' && typeof last !== 'undefined') { - limitTemplate = aql`TO_NUMBER(user._key) DESC LIMIT TO_NUMBER(${last})` - } - } else { - const argSet = typeof first !== 'undefined' ? 'first' : 'last' - const typeSet = typeof first !== 'undefined' ? typeof first : typeof last - console.warn( - `User: ${userKey} attempted to have \`${argSet}\` set as a ${typeSet} for: loadUserConnectionsByUserId.`, - ) - throw new Error( - i18n._(t`\`${argSet}\` must be of type \`number\` not \`${typeSet}\`.`), - ) + } else if (typeof first !== 'undefined' && typeof last === 'undefined') { + limitTemplate = aql`TO_NUMBER(user._key) ASC LIMIT TO_NUMBER(${first})` + } else if (typeof first === 'undefined' && typeof last !== 'undefined') { + limitTemplate = aql`TO_NUMBER(user._key) DESC LIMIT TO_NUMBER(${last})` } + } else { + const argSet = typeof first !== 'undefined' ? 'first' : 'last' + const typeSet = typeof first !== 'undefined' ? typeof first : typeof last + console.warn( + `User: ${userKey} attempted to have \`${argSet}\` set as a ${typeSet} for: loadUserConnectionsByUserId.`, + ) + throw new Error(i18n._(t`\`${argSet}\` must be of type \`number\` not \`${typeSet}\`.`)) + } - let hasNextPageFilter = aql`FILTER TO_NUMBER(user._key) > TO_NUMBER(LAST(retrievedUsers)._key)` - let hasPreviousPageFilter = aql`FILTER TO_NUMBER(user._key) < TO_NUMBER(FIRST(retrievedUsers)._key)` - if (typeof orderBy !== 'undefined') { - let hasNextPageDirection = aql`` - let hasPreviousPageDirection = aql`` - if (orderBy.direction === 'ASC') { - hasNextPageDirection = aql`>` - hasPreviousPageDirection = aql`<` - } else { - hasNextPageDirection = aql`<` - hasPreviousPageDirection = aql`>` - } + let hasNextPageFilter = aql`FILTER TO_NUMBER(user._key) > TO_NUMBER(LAST(retrievedUsers)._key)` + let hasPreviousPageFilter = aql`FILTER TO_NUMBER(user._key) < TO_NUMBER(FIRST(retrievedUsers)._key)` + if (typeof orderBy !== 'undefined') { + let hasNextPageDirection = aql`` + let hasPreviousPageDirection = aql`` + if (orderBy.direction === 'ASC') { + hasNextPageDirection = aql`>` + hasPreviousPageDirection = aql`<` + } else { + hasNextPageDirection = aql`<` + hasPreviousPageDirection = aql`>` + } - let userField = aql`` - let hasNextPageDocumentField = aql`` - let hasPreviousPageDocumentField = aql`` - /* istanbul ignore else */ - if (orderBy.field === 'user-username') { - userField = aql`user.userName` - hasNextPageDocumentField = aql`LAST(retrievedUsers).userName` - hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).userName` - } else if (orderBy.field === 'user-displayName') { - userField = aql`user.displayName` - hasNextPageDocumentField = aql`LAST(retrievedUsers).displayName` - hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).displayName` - } else if (orderBy.field === 'user-emailValidated') { - userField = aql`user.emailValidated` - hasNextPageDocumentField = aql`LAST(retrievedUsers).emailValidated` - hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).emailValidated` - } else if (orderBy.field === 'user-affiliations-totalCount') { - userField = aql`user.affiliations.totalCount` - hasNextPageDocumentField = aql`LAST(retrievedUsers).affiliations.totalCount` - hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).affiliations.totalCount` - } else if (orderBy.field === 'user-insider') { + let userField = aql`` + let hasNextPageDocumentField = aql`` + let hasPreviousPageDocumentField = aql`` + /* istanbul ignore else */ + if (orderBy.field === 'user-username') { + userField = aql`user.userName` + hasNextPageDocumentField = aql`LAST(retrievedUsers).userName` + hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).userName` + } else if (orderBy.field === 'user-displayName') { + userField = aql`user.displayName` + hasNextPageDocumentField = aql`LAST(retrievedUsers).displayName` + hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).displayName` + } else if (orderBy.field === 'user-emailValidated') { + userField = aql`user.emailValidated` + hasNextPageDocumentField = aql`LAST(retrievedUsers).emailValidated` + hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).emailValidated` + } else if (orderBy.field === 'user-affiliations-totalCount') { + userField = aql`user.affiliations.totalCount` + hasNextPageDocumentField = aql`LAST(retrievedUsers).affiliations.totalCount` + hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).affiliations.totalCount` + } else if (orderBy.field === 'user-insider') { userField = aql`user.insideUser` hasNextPageDocumentField = aql`LAST(retrievedUsers).insideUser` hasPreviousPageDocumentField = aql`FIRST(retrievedUsers).insideUser` } - hasNextPageFilter = aql` + hasNextPageFilter = aql` FILTER ${userField} ${hasNextPageDirection} ${hasNextPageDocumentField} OR (${userField} == ${hasNextPageDocumentField} AND TO_NUMBER(user._key) > TO_NUMBER(LAST(retrievedUsers)._key)) ` - hasPreviousPageFilter = aql` + hasPreviousPageFilter = aql` FILTER ${userField} ${hasPreviousPageDirection} ${hasPreviousPageDocumentField} OR (${userField} == ${hasPreviousPageDocumentField} AND TO_NUMBER(user._key) < TO_NUMBER(FIRST(retrievedUsers)._key)) ` - } + } - let sortByField = aql`` - if (typeof orderBy !== 'undefined') { - /* istanbul ignore else */ - if (orderBy.field === 'user-username') { - sortByField = aql`user.userName ${orderBy.direction},` - } else if (orderBy.field === 'user-displayName') { - sortByField = aql`user.displayName ${orderBy.direction},` - } else if (orderBy.field === 'user-emailValidated') { - sortByField = aql`user.emailValidated ${orderBy.direction},` - } else if (orderBy.field === 'user-affiliations-totalCount') { - sortByField = aql`user.affiliations.totalCount ${orderBy.direction},` - } else if (orderBy.field === 'user-insider') { + let sortByField = aql`` + if (typeof orderBy !== 'undefined') { + /* istanbul ignore else */ + if (orderBy.field === 'user-username') { + sortByField = aql`user.userName ${orderBy.direction},` + } else if (orderBy.field === 'user-displayName') { + sortByField = aql`user.displayName ${orderBy.direction},` + } else if (orderBy.field === 'user-emailValidated') { + sortByField = aql`user.emailValidated ${orderBy.direction},` + } else if (orderBy.field === 'user-affiliations-totalCount') { + sortByField = aql`user.affiliations.totalCount ${orderBy.direction},` + } else if (orderBy.field === 'user-insider') { sortByField = aql`user.insideUser ${orderBy.direction},` } - } + } - let sortString - if (typeof last !== 'undefined') { - sortString = aql`DESC` - } else { - sortString = aql`ASC` - } + let sortString + if (typeof last !== 'undefined') { + sortString = aql`DESC` + } else { + sortString = aql`ASC` + } - let userKeysQuery - if (isSuperAdmin) { - userKeysQuery = aql` + let userKeysQuery + if (isSuperAdmin) { + userKeysQuery = aql` WITH users, userSearch, claims, organizations LET userKeys = ( FOR user IN users RETURN user._key ) ` - } else { - userKeysQuery = aql` + } else { + userKeysQuery = aql` WITH affiliations, organizations, users, userSearch, claims LET userKeys = UNIQUE(FLATTEN( LET keys = [] @@ -257,34 +247,45 @@ export const loadUserConnectionsByUserId = RETURN APPEND(keys, affiliationUserKeys) )) ` - } + } - let userSearchQuery = aql`` - let loopString = aql`FOR user IN users` - let totalCount = aql`LENGTH(userKeys)` - if (typeof search !== 'undefined' && search !== '') { - search = cleanseInput(search) - userSearchQuery = aql` + let userSearchQuery = aql`` + let loopString = aql`FOR user IN users` + let totalCount = aql`LENGTH(userKeys)` + if (typeof search !== 'undefined' && search !== '') { + search = cleanseInput(search) + userSearchQuery = aql` LET tokenArr = TOKENS(${search}, "text_en") - LET searchedUsers = ( + LET searchedDisplayNamesCount = FLATTEN( FOR tokenItem in tokenArr LET token = LOWER(tokenItem) FOR user IN userSearch SEARCH ANALYZER( user.displayName LIKE CONCAT("%", token, "%") - OR user.userName LIKE CONCAT("%", token, "%") , "text_en") FILTER user._key IN userKeys - RETURN user + COLLECT currentUser = user WITH COUNT INTO count + RETURN { + 'user': currentUser, + 'count': count + } + ) + LET searchedDisplayNames = searchedDisplayNamesCount[* FILTER CURRENT.count == LENGTH(tokenArr)].user + LET searchedUserNames = ( + FOR user IN users + FILTER LOWER(user.userName) LIKE CONCAT("%", LOWER(${search}), "%") + FILTER user._key IN userKeys + RETURN user ) + LET searchedUsers = UNIQUE(APPEND(searchedDisplayNames, searchedUserNames)) ` - loopString = aql`FOR user IN searchedUsers` - totalCount = aql`LENGTH(searchedUsers)` - } + loopString = aql`FOR user IN searchedUsers` + totalCount = aql`LENGTH(searchedUsers)` + } - let requestedUserInfo - try { - requestedUserInfo = await query` + let requestedUserInfo + try { + requestedUserInfo = await query` ${userKeysQuery} ${userSearchQuery} @@ -328,51 +329,51 @@ export const loadUserConnectionsByUserId = "endKey": LAST(retrievedUsers)._key } ` - } catch (err) { - console.error( - `Database error occurred while user: ${userKey} was trying to query users in loadUserConnectionsByUserId, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to query user(s). Please try again.`)) - } - - let usersInfo - try { - usersInfo = await requestedUserInfo.next() - } catch (err) { - console.error( - `Cursor error occurred while user: ${userKey} was trying to gather users in loadUserConnectionsByUserId, error: ${err}`, - ) - throw new Error(i18n._(t`Unable to load user(s). Please try again.`)) - } - - if (usersInfo.users.length === 0) { - return { - edges: [], - totalCount: 0, - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: '', - endCursor: '', - }, - } - } + } catch (err) { + console.error( + `Database error occurred while user: ${userKey} was trying to query users in loadUserConnectionsByUserId, error: ${err}`, + ) + throw new Error(i18n._(t`Unable to query user(s). Please try again.`)) + } - const edges = usersInfo.users.map((user) => { - return { - cursor: toGlobalId('user', user._key), - node: user, - } - }) + let usersInfo + try { + usersInfo = await requestedUserInfo.next() + } catch (err) { + console.error( + `Cursor error occurred while user: ${userKey} was trying to gather users in loadUserConnectionsByUserId, error: ${err}`, + ) + throw new Error(i18n._(t`Unable to load user(s). Please try again.`)) + } + if (usersInfo.users.length === 0) { return { - edges, - totalCount: usersInfo.totalCount, + edges: [], + totalCount: 0, pageInfo: { - hasNextPage: usersInfo.hasNextPage, - hasPreviousPage: usersInfo.hasPreviousPage, - startCursor: toGlobalId('user', usersInfo.startKey), - endCursor: toGlobalId('user', usersInfo.endKey), + hasNextPage: false, + hasPreviousPage: false, + startCursor: '', + endCursor: '', }, } } + + const edges = usersInfo.users.map((user) => { + return { + cursor: toGlobalId('user', user._key), + node: user, + } + }) + + return { + edges, + totalCount: usersInfo.totalCount, + pageInfo: { + hasNextPage: usersInfo.hasNextPage, + hasPreviousPage: usersInfo.hasPreviousPage, + startCursor: toGlobalId('user', usersInfo.startKey), + endCursor: toGlobalId('user', usersInfo.endKey), + }, + } + } diff --git a/api/src/user/mutations/__tests__/authenticate.test.js b/api/src/user/mutations/__tests__/authenticate.test.js index d1550aed7..94235d3ac 100644 --- a/api/src/user/mutations/__tests__/authenticate.test.js +++ b/api/src/user/mutations/__tests__/authenticate.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import bcrypt from 'bcryptjs' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -73,7 +74,6 @@ describe('authenticate user account', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneValidated: false, emailValidated: false, tfaCode: 123456, @@ -118,7 +118,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -161,7 +160,6 @@ describe('authenticate user account', () => { id: `${toGlobalId('user', user._key)}`, userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'FRENCH', phoneValidated: false, emailValidated: true, }, @@ -196,7 +194,6 @@ describe('authenticate user account', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneValidated: false, emailValidated: false, tfaCode: 123456, @@ -254,7 +251,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -298,7 +294,6 @@ describe('authenticate user account', () => { id: `${toGlobalId('user', user._key)}`, userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'FRENCH', phoneValidated: false, emailValidated: true, }, @@ -370,7 +365,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -443,7 +437,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -516,7 +509,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -611,7 +603,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -683,7 +674,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -703,6 +693,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -762,7 +753,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -783,6 +773,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -856,7 +847,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -929,7 +919,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -1002,7 +991,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -1097,7 +1085,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -1169,7 +1156,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -1189,6 +1175,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -1248,7 +1235,6 @@ describe('authenticate user account', () => { id userName displayName - preferredLang phoneValidated emailValidated } @@ -1269,6 +1255,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), uuidv4, auth: { diff --git a/api/src/user/mutations/__tests__/close-account.test.js b/api/src/user/mutations/__tests__/close-account.test.js index 4e698f32b..500f06a53 100644 --- a/api/src/user/mutations/__tests__/close-account.test.js +++ b/api/src/user/mutations/__tests__/close-account.test.js @@ -1,5 +1,6 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -163,6 +164,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -241,6 +243,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -323,6 +326,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -390,6 +394,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -558,6 +563,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -642,6 +648,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -729,6 +736,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -803,6 +811,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: superAdmin._key, + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: checkSuperAdmin({ i18n, @@ -889,6 +898,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(false), userRequired: jest.fn().mockReturnValue({ _key: '123' }), @@ -950,6 +960,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123' }), @@ -999,6 +1010,7 @@ describe('given the closeAccount mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), commit: jest.fn(), + abort: jest.fn(), }) const response = await graphql({ @@ -1025,6 +1037,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123', _id: 'users/123' }), @@ -1063,6 +1076,7 @@ describe('given the closeAccount mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('trx step error')), commit: jest.fn(), + abort: jest.fn(), }) const response = await graphql({ @@ -1089,6 +1103,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123', _id: 'users/123' }), @@ -1128,6 +1143,7 @@ describe('given the closeAccount mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1154,6 +1170,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123', _id: 'users/123' }), @@ -1226,6 +1243,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(false), userRequired: jest.fn().mockReturnValue({ _key: '123' }), @@ -1287,6 +1305,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123' }), @@ -1336,6 +1355,7 @@ describe('given the closeAccount mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('trx step error')), commit: jest.fn(), + abort: jest.fn(), }) const response = await graphql({ @@ -1362,6 +1382,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123', _id: 'users/123' }), @@ -1400,6 +1421,7 @@ describe('given the closeAccount mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('trx step error')), commit: jest.fn(), + abort: jest.fn(), }) const response = await graphql({ @@ -1426,6 +1448,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123', _id: 'users/123' }), @@ -1465,6 +1488,7 @@ describe('given the closeAccount mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue(), commit: jest.fn().mockRejectedValue(new Error('trx commit error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1491,6 +1515,7 @@ describe('given the closeAccount mutation', () => { collections: collectionNames, transaction: mockedTransaction, userKey: '123', + request: { ip: '127.0.0.1' }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(true), userRequired: jest.fn().mockReturnValue({ _key: '123', _id: 'users/123' }), diff --git a/api/src/user/mutations/__tests__/complete-tour.test.js b/api/src/user/mutations/__tests__/complete-tour.test.js new file mode 100644 index 000000000..9d20c99d6 --- /dev/null +++ b/api/src/user/mutations/__tests__/complete-tour.test.js @@ -0,0 +1,282 @@ +import { dbNameFromFile } from 'arango-tools' +import { createUserContextGenerator, ensureDatabase as ensure } from '../../../testUtilities' +import { graphql, GraphQLSchema } from 'graphql' + +import { createQuerySchema } from '../../../query' +import { createMutationSchema } from '../../../mutation' +import dbschema from '../../../../database.json' +import { collectionNames } from '../../../collection-names' +import { createI18n } from '../../../create-i18n' +import { toGlobalId } from 'graphql-relay/index' + +const { DB_PASS: rootPass, DB_URL: url, AUTHENTICATED_KEY, HASHING_SALT } = process.env + +const schema = new GraphQLSchema({ + query: createQuerySchema(), + mutation: createMutationSchema(), +}) +const consoleOutput = [] +const mockedInfo = (output) => consoleOutput.push(output) +const mockedWarn = (output) => consoleOutput.push(output) +const mockedError = (output) => consoleOutput.push(output) +console.info = mockedInfo +console.warn = mockedWarn +console.error = mockedError + +const i18n = createI18n('en') + +let db, query, drop, truncate, collections, transaction, createUserContext, normalUser, normalUserContext + +describe('complete tour mutation', () => { + beforeAll(async () => { + ;({ db, query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + + createUserContext = createUserContextGenerator({ + db, + query, + transaction, + collectionNames, + i18n, + secret: AUTHENTICATED_KEY, + salt: HASHING_SALT, + }) + }) + + beforeEach(async () => { + normalUser = ( + await collections.users.save( + { + _key: 'normaluser', + userName: 'normaluser@test.gc.ca', + emailValidated: true, + }, + { returnNew: true }, + ) + ).new + normalUserContext = await createUserContext({ userKey: normalUser._key }) + }) + + afterEach(async () => { + consoleOutput.length = 0 + await truncate() + }) + + afterAll(async () => { + await drop() + }) + + it('adds completed tour on successful mutation', async () => { + const currentUserState = await (await query`RETURN DOCUMENT(users, ${normalUser._key})`).next() + + expect(currentUserState?.completedTours).toBeUndefined() + + const tourToCompleteOne = 'tour1' + + const response = await graphql({ + schema, + source: ` + mutation { + completeTour(input: { tourId: "${tourToCompleteOne}" }) { + result { + ... on CompleteTourResult { + status + user { + id + completedTours { + tourId + completedAt + } + } + } + ... on CompleteTourError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const tourOneCompleteTime = response?.data?.completeTour?.result?.user?.completedTours?.[0]?.completedAt + expect(!!tourOneCompleteTime).not.toBeFalsy() + + const expectedResponse = { + data: { + completeTour: { + result: { + status: 'Tour completion confirmed successfully', + user: { + id: toGlobalId('user', normalUser._key), + completedTours: [ + { + tourId: tourToCompleteOne, + completedAt: tourOneCompleteTime, + }, + ], + }, + }, + }, + }, + } + + expect(response).toEqual(expectedResponse) + + const tourToCompleteTwo = 'tour2' + + const responseTwo = await graphql({ + schema, + source: ` + mutation { + completeTour(input: { tourId: "${tourToCompleteTwo}" }) { + result { + ... on CompleteTourResult { + status + user { + id + completedTours { + tourId + completedAt + } + } + } + ... on CompleteTourError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const tourTwoCompleteTime = responseTwo?.data?.completeTour?.result?.user?.completedTours?.[1]?.completedAt + expect(!!tourTwoCompleteTime).not.toBeFalsy() + + const expectedResponseTwo = { + data: { + completeTour: { + result: { + status: 'Tour completion confirmed successfully', + user: { + id: toGlobalId('user', normalUser._key), + completedTours: [ + { + tourId: tourToCompleteOne, + completedAt: tourOneCompleteTime, + }, + { + tourId: tourToCompleteTwo, + completedAt: tourTwoCompleteTime, + }, + ], + }, + }, + }, + }, + } + + expect(responseTwo).toEqual(expectedResponseTwo) + }) + it('updates timestamp for re-completed tour', async () => { + const tourToComplete = 'tour1' + await query`UPDATE { _key: ${normalUser._key}} WITH { completedTours: [{tourId: ${tourToComplete}, completedAt: DATE_ISO8601(DATE_NOW())}]} IN users` + const currentUserState = await (await query`RETURN DOCUMENT(users, ${normalUser._key})`).next() + + expect(currentUserState?.completedTours).toHaveLength(1) + + const originalCompletedAt = currentUserState?.completedTours[0].completedAt + expect(!!originalCompletedAt).not.toBeFalsy() + + const _response = await graphql({ + schema, + source: ` + mutation { + completeTour(input: { tourId: "${tourToComplete}" }) { + result { + ... on CompleteTourResult { + status + user { + id + completedTours { + tourId + completedAt + } + } + } + ... on CompleteTourError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const newUserState = await (await query`RETURN DOCUMENT(users, ${normalUser._key})`).next() + expect(newUserState?.completedTours).toHaveLength(1) + expect(new Date(newUserState.completedTours[0].completedAt) > new Date(originalCompletedAt)).toBe(true) + }) + it('throws an error when tourId is empty after input cleansing', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + completeTour(input: { tourId: " " }) { + result { + ... on CompleteTourResult { + status + user { + id + completedTours { + tourId + completedAt + } + } + } + ... on CompleteTourError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const expectedResponse = { + data: { + completeTour: { + result: { + code: 400, + description: 'Unable to confirm completion of the tour. Please try again.', + }, + }, + }, + } + + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toHaveLength(1) + expect(consoleOutput[0]).toEqual( + `User: ${normalUser._key} did not provide a tour id when attempting to confirm completion of the tour.`, + ) + }) +}) diff --git a/api/src/user/mutations/__tests__/dismiss-message.test.js b/api/src/user/mutations/__tests__/dismiss-message.test.js new file mode 100644 index 000000000..4a17794e8 --- /dev/null +++ b/api/src/user/mutations/__tests__/dismiss-message.test.js @@ -0,0 +1,282 @@ +import { dbNameFromFile } from 'arango-tools' +import { createUserContextGenerator, ensureDatabase as ensure } from '../../../testUtilities' +import { graphql, GraphQLSchema } from 'graphql' + +import { createQuerySchema } from '../../../query' +import { createMutationSchema } from '../../../mutation' +import dbschema from '../../../../database.json' +import { collectionNames } from '../../../collection-names' +import { createI18n } from '../../../create-i18n' +import { toGlobalId } from 'graphql-relay/index' + +const { DB_PASS: rootPass, DB_URL: url, AUTHENTICATED_KEY, HASHING_SALT } = process.env + +const schema = new GraphQLSchema({ + query: createQuerySchema(), + mutation: createMutationSchema(), +}) +const consoleOutput = [] +const mockedInfo = (output) => consoleOutput.push(output) +const mockedWarn = (output) => consoleOutput.push(output) +const mockedError = (output) => consoleOutput.push(output) +console.info = mockedInfo +console.warn = mockedWarn +console.error = mockedError + +const i18n = createI18n('en') + +let db, query, drop, truncate, collections, transaction, createUserContext, normalUser, normalUserContext + +describe('dismiss message mutation', () => { + beforeAll(async () => { + ;({ db, query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + schema: dbschema, + })) + + createUserContext = createUserContextGenerator({ + db, + query, + transaction, + collectionNames, + i18n, + secret: AUTHENTICATED_KEY, + salt: HASHING_SALT, + }) + }) + + beforeEach(async () => { + normalUser = ( + await collections.users.save( + { + _key: 'normaluser', + userName: 'normaluser@test.gc.ca', + emailValidated: true, + }, + { returnNew: true }, + ) + ).new + normalUserContext = await createUserContext({ userKey: normalUser._key }) + }) + + afterEach(async () => { + consoleOutput.length = 0 + await truncate() + }) + + afterAll(async () => { + await drop() + }) + + it('adds dismissed message on successful mutation', async () => { + const currentUserState = await (await query`RETURN DOCUMENT(users, ${normalUser._key})`).next() + + expect(currentUserState?.dismissedMessages).toBeUndefined() + + const messageToIgnoreOne = 'message1' + + const response = await graphql({ + schema, + source: ` + mutation { + dismissMessage(input: { messageId: "${messageToIgnoreOne}" }) { + result { + ... on DismissMessageResult { + status + user { + id + dismissedMessages { + messageId + dismissedAt + } + } + } + ... on DismissMessageError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const messageOneDismissTime = response?.data?.dismissMessage?.result?.user?.dismissedMessages?.[0]?.dismissedAt + expect(!!messageOneDismissTime).not.toBeFalsy() + + const expectedResponse = { + data: { + dismissMessage: { + result: { + status: 'Message dismissed successfully', + user: { + id: toGlobalId('user', normalUser._key), + dismissedMessages: [ + { + messageId: messageToIgnoreOne, + dismissedAt: messageOneDismissTime, + }, + ], + }, + }, + }, + }, + } + + expect(response).toEqual(expectedResponse) + + const messageToIgnoreTwo = 'message2' + + const responseTwo = await graphql({ + schema, + source: ` + mutation { + dismissMessage(input: { messageId: "${messageToIgnoreTwo}" }) { + result { + ... on DismissMessageResult { + status + user { + id + dismissedMessages { + messageId + dismissedAt + } + } + } + ... on DismissMessageError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const messageTwoDismissTime = responseTwo?.data?.dismissMessage?.result?.user?.dismissedMessages?.[1]?.dismissedAt + expect(!!messageTwoDismissTime).not.toBeFalsy() + + const expectedResponseTwo = { + data: { + dismissMessage: { + result: { + status: 'Message dismissed successfully', + user: { + id: toGlobalId('user', normalUser._key), + dismissedMessages: [ + { + messageId: messageToIgnoreOne, + dismissedAt: messageOneDismissTime, + }, + { + messageId: messageToIgnoreTwo, + dismissedAt: messageTwoDismissTime, + }, + ], + }, + }, + }, + }, + } + + expect(responseTwo).toEqual(expectedResponseTwo) + }) + it('updates timestamp for re-ignored message', async () => { + const messageToIgnore = 'message1' + await query`UPDATE { _key: ${normalUser._key}} WITH { dismissedMessages: [{messageId: ${messageToIgnore}, dismissedAt: DATE_ISO8601(DATE_NOW())}]} IN users` + const currentUserState = await (await query`RETURN DOCUMENT(users, ${normalUser._key})`).next() + + expect(currentUserState?.dismissedMessages).toHaveLength(1) + + const originalDismissedAt = currentUserState?.dismissedMessages[0].dismissedAt + expect(!!originalDismissedAt).not.toBeFalsy() + + const _response = await graphql({ + schema, + source: ` + mutation { + dismissMessage(input: { messageId: "${messageToIgnore}" }) { + result { + ... on DismissMessageResult { + status + user { + id + dismissedMessages { + messageId + dismissedAt + } + } + } + ... on DismissMessageError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const newUserState = await (await query`RETURN DOCUMENT(users, ${normalUser._key})`).next() + expect(newUserState?.dismissedMessages).toHaveLength(1) + expect(new Date(newUserState.dismissedMessages[0].dismissedAt) > new Date(originalDismissedAt)).toBe(true) + }) + it('throws an error when messageId is empty after input cleansing', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + dismissMessage(input: { messageId: " " }) { + result { + ... on DismissMessageResult { + status + user { + id + dismissedMessages { + messageId + dismissedAt + } + } + } + ... on DismissMessageError { + code + description + } + } + } + } + `, + rootValue: null, + contextValue: normalUserContext, + }) + + const expectedResponse = { + data: { + dismissMessage: { + result: { + code: 400, + description: 'Unable to dismiss message. Please try again.', + }, + }, + }, + } + + expect(response).toEqual(expectedResponse) + expect(consoleOutput).toHaveLength(1) + expect(consoleOutput[0]).toEqual( + `User: ${normalUser._key} did not provide a message id when attempting to dismiss a message.`, + ) + }) +}) diff --git a/api/src/user/mutations/__tests__/refresh-tokens.test.js b/api/src/user/mutations/__tests__/refresh-tokens.test.js index 08fa90f68..c726fdb35 100644 --- a/api/src/user/mutations/__tests__/refresh-tokens.test.js +++ b/api/src/user/mutations/__tests__/refresh-tokens.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' import { v4 as uuidv4 } from 'uuid' @@ -70,7 +71,6 @@ describe('refresh users tokens', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', phoneValidated: false, emailValidated: false, tfaCode: null, @@ -171,7 +171,6 @@ describe('refresh users tokens', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', phoneValidated: false, emailValidated: false, tfaCode: null, @@ -681,6 +680,7 @@ describe('refresh users tokens', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }) const refreshToken = tokenize({ @@ -762,6 +762,7 @@ describe('refresh users tokens', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }) const refreshToken = tokenize({ @@ -1243,6 +1244,7 @@ describe('refresh users tokens', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }) const refreshToken = tokenize({ @@ -1324,6 +1326,7 @@ describe('refresh users tokens', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }) const refreshToken = tokenize({ diff --git a/api/src/user/mutations/__tests__/remove-phone-number.test.js b/api/src/user/mutations/__tests__/remove-phone-number.test.js index 99b8add2d..ab123372a 100644 --- a/api/src/user/mutations/__tests__/remove-phone-number.test.js +++ b/api/src/user/mutations/__tests__/remove-phone-number.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -799,6 +800,7 @@ describe('testing the removePhoneNumber mutation', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('transaction step error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -845,6 +847,7 @@ describe('testing the removePhoneNumber mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('transaction step error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -906,6 +909,7 @@ describe('testing the removePhoneNumber mutation', () => { it('throws an error', async () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('transaction step error occurred.')), + abort: jest.fn(), }) const response = await graphql({ @@ -952,6 +956,7 @@ describe('testing the removePhoneNumber mutation', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('transaction step error occurred.')), + abort: jest.fn(), }) const response = await graphql({ diff --git a/api/src/user/mutations/__tests__/reset-password.test.js b/api/src/user/mutations/__tests__/reset-password.test.js index 4dc308c37..366aa1b79 100644 --- a/api/src/user/mutations/__tests__/reset-password.test.js +++ b/api/src/user/mutations/__tests__/reset-password.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import bcrypt from 'bcryptjs' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -65,7 +66,6 @@ describe('reset users password', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -100,6 +100,7 @@ describe('reset users password', () => { request: { protocol: 'https', get: (text) => text, + ip: '127.0.0.1', }, }, }) @@ -228,6 +229,7 @@ describe('reset users password', () => { jwt, response: mockedResponse, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: jest.fn().mockReturnValue('token'), @@ -300,6 +302,7 @@ describe('reset users password', () => { query, collections: collectionNames, transaction, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize, @@ -443,6 +446,7 @@ describe('reset users password', () => { uuidv4, response: mockedResponse, jwt, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: jest.fn().mockReturnValue('token'), @@ -943,6 +947,7 @@ describe('reset users password', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), auth: { bcrypt, @@ -1013,6 +1018,7 @@ describe('reset users password', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), auth: { bcrypt, @@ -1444,6 +1450,7 @@ describe('reset users password', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), auth: { bcrypt, @@ -1514,6 +1521,7 @@ describe('reset users password', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), auth: { bcrypt, diff --git a/api/src/user/mutations/__tests__/send-email-verification.test.js b/api/src/user/mutations/__tests__/send-email-verification.test.js deleted file mode 100644 index 9798307f2..000000000 --- a/api/src/user/mutations/__tests__/send-email-verification.test.js +++ /dev/null @@ -1,364 +0,0 @@ -import { ensure, dbNameFromFile } from 'arango-tools' -import bcrypt from 'bcryptjs' -import { graphql, GraphQLSchema } from 'graphql' -import { setupI18n } from '@lingui/core' - -import englishMessages from '../../../locale/en/messages' -import frenchMessages from '../../../locale/fr/messages' -import { createQuerySchema } from '../../../query' -import { createMutationSchema } from '../../../mutation' -import { cleanseInput } from '../../../validators' -import { loadUserByUserName } from '../../loaders' -import dbschema from '../../../../database.json' - -const { DB_PASS: rootPass, DB_URL: url } = process.env -const mockNotify = jest.fn() - -describe('user send password reset email', () => { - let query, drop, truncate, collections, schema, request, i18n - const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - - beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - schema = new GraphQLSchema({ - query: createQuerySchema(), - mutation: createMutationSchema(), - }) - request = { - protocol: 'https', - get: (text) => text, - } - }) - afterEach(() => { - consoleOutput.length = 0 - }) - - describe('successfully sends verification email', () => { - beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) - describe('users preferred language is english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - beforeEach(async () => { - await collections.users.save({ - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'english', - tfaValidated: false, - emailValidated: false, - }) - }) - it('returns status text', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - sendEmailVerification( - input: { userName: "test.account@istio.actually.exists" } - ) { - status - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - query, - auth: { - bcrypt, - tokenize: jest.fn().mockReturnValue('token'), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { - sendVerificationEmail: mockNotify, - }, - }, - }) - - const expectedResult = { - data: { - sendEmailVerification: { - status: - 'If an account with this username is found, an email verification link will be found in your inbox.', - }, - }, - } - - const user = await loadUserByUserName({ - query, - userKey: '1', - i18n: {}, - }).load('test.account@istio.actually.exists') - - const verifyUrl = `https://${request.get('host')}/validate/token` - - expect(response).toEqual(expectedResult) - expect(mockNotify).toHaveBeenCalledWith({ - user, - verifyUrl, - }) - expect(consoleOutput).toEqual([`User: ${user._key} successfully sent a verification email.`]) - }) - }) - describe('users preferred language is french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - beforeEach(async () => { - await collections.users.save({ - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'french', - tfaValidated: false, - emailValidated: false, - }) - }) - it('returns status text', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - sendEmailVerification( - input: { userName: "test.account@istio.actually.exists" } - ) { - status - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - query, - auth: { - bcrypt, - tokenize: jest.fn().mockReturnValue('token'), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - }, - notify: { - sendVerificationEmail: mockNotify, - }, - }, - }) - - const expectedResult = { - data: { - sendEmailVerification: { - status: - "Si un compte avec ce nom d'utilisateur est trouvé, un lien de vérification par e-mail sera trouvé dans votre boîte de réception.", - }, - }, - } - - const user = await loadUserByUserName({ - query, - userKey: '1', - i18n: {}, - }).load('test.account@istio.actually.exists') - - const verifyUrl = `https://${request.get('host')}/validate/token` - - expect(response).toEqual(expectedResult) - expect(mockNotify).toHaveBeenCalledWith({ - user, - verifyUrl, - }) - expect(consoleOutput).toEqual([`User: ${user._key} successfully sent a verification email.`]) - }) - }) - }) - describe('unsuccessfully sends verification email', () => { - describe('users preferred language is english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('no user associated with account', () => { - it('returns status text', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - sendEmailVerification( - input: { - userName: "test.account@istio.does.not.actually.exists" - } - ) { - status - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - query, - auth: { - bcrypt, - tokenize: jest.fn().mockReturnValue('token'), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - notify: { - sendVerificationEmail: mockNotify, - }, - }, - }) - - const expectedResult = { - data: { - sendEmailVerification: { - status: - 'If an account with this username is found, an email verification link will be found in your inbox.', - }, - }, - } - - expect(response).toEqual(expectedResult) - expect(consoleOutput).toEqual([ - `A user attempted to send a verification email for test.account@istio.does.not.actually.exists but no account is affiliated with this user name.`, - ]) - }) - }) - }) - describe('users preferred language is french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('no user associated with account', () => { - it('returns status text', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - sendEmailVerification( - input: { - userName: "test.account@istio.does.not.actually.exists" - } - ) { - status - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - query, - auth: { - bcrypt, - tokenize: jest.fn().mockReturnValue('token'), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - notify: { - sendVerificationEmail: mockNotify, - }, - }, - }) - - const expectedResult = { - data: { - sendEmailVerification: { - status: - "Si un compte avec ce nom d'utilisateur est trouvé, un lien de vérification par e-mail sera trouvé dans votre boîte de réception.", - }, - }, - } - - expect(response).toEqual(expectedResult) - expect(consoleOutput).toEqual([ - `A user attempted to send a verification email for test.account@istio.does.not.actually.exists but no account is affiliated with this user name.`, - ]) - }) - }) - }) - }) -}) diff --git a/api/src/user/mutations/__tests__/send-password-reset.test.js b/api/src/user/mutations/__tests__/send-password-reset.test.js index 7d303a5ab..9fe11e118 100644 --- a/api/src/user/mutations/__tests__/send-password-reset.test.js +++ b/api/src/user/mutations/__tests__/send-password-reset.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import bcrypt from 'bcryptjs' import { graphql, GraphQLSchema } from 'graphql' import { setupI18n } from '@lingui/core' @@ -78,7 +79,6 @@ describe('user send password reset email', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }) @@ -163,7 +163,6 @@ describe('user send password reset email', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/user/mutations/__tests__/set-phone-number.test.js b/api/src/user/mutations/__tests__/set-phone-number.test.js index 22914afcd..2c4e154b9 100644 --- a/api/src/user/mutations/__tests__/set-phone-number.test.js +++ b/api/src/user/mutations/__tests__/set-phone-number.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import bcrypt from 'bcryptjs' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -80,7 +81,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -131,7 +131,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -158,7 +158,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -171,7 +170,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneDetails: {}, phoneValidated: true, tfaValidated: false, @@ -225,7 +223,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -252,7 +250,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -304,7 +301,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -361,7 +358,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -378,7 +375,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneDetails: {}, phoneValidated: true, tfaValidated: false, @@ -432,7 +428,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -459,7 +455,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -511,7 +506,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -568,7 +563,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -585,7 +580,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneDetails: {}, phoneValidated: true, tfaValidated: false, @@ -639,7 +633,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -666,7 +660,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -718,7 +711,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -775,7 +768,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -809,7 +802,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -860,7 +852,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -887,7 +879,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -900,7 +891,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneDetails: {}, phoneValidated: true, tfaValidated: false, @@ -954,7 +944,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -981,7 +971,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -1033,7 +1022,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1090,7 +1079,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1107,7 +1096,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneDetails: {}, phoneValidated: true, tfaValidated: false, @@ -1161,7 +1149,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1188,7 +1176,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -1240,7 +1227,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1297,7 +1284,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1314,7 +1301,6 @@ describe('user sets a new phone number', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneDetails: {}, phoneValidated: true, tfaValidated: false, @@ -1368,7 +1354,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1395,7 +1381,6 @@ describe('user sets a new phone number', () => { expect(response).toEqual(expectedResult) expect(mockNotify).toHaveBeenCalledWith({ - phoneNumber: newPhoneNumber, user, }) expect(consoleOutput).toEqual([`User: ${user._key} successfully set phone number.`]) @@ -1447,7 +1432,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1504,7 +1489,7 @@ describe('user sets a new phone number', () => { loadUserByKey: loadUserByKey({ query }), }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1542,6 +1527,7 @@ describe('user sets a new phone number', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1589,7 +1575,7 @@ describe('user sets a new phone number', () => { }, }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1611,6 +1597,7 @@ describe('user sets a new phone number', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1656,7 +1643,7 @@ describe('user sets a new phone number', () => { }, }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1693,6 +1680,7 @@ describe('user sets a new phone number', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1740,7 +1728,7 @@ describe('user sets a new phone number', () => { }, }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) @@ -1762,6 +1750,7 @@ describe('user sets a new phone number', () => { const mockedTransaction = jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }) const response = await graphql({ @@ -1807,7 +1796,7 @@ describe('user sets a new phone number', () => { }, }, notify: { - sendTfaTextMsg: mockNotify, + sendAuthTextMsg: mockNotify, }, }, }) diff --git a/api/src/user/mutations/__tests__/sign-in.test.js b/api/src/user/mutations/__tests__/sign-in.test.js index 7ad9f9dce..933c264e7 100644 --- a/api/src/user/mutations/__tests__/sign-in.test.js +++ b/api/src/user/mutations/__tests__/sign-in.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import bcrypt from 'bcryptjs' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -73,7 +74,6 @@ describe('authenticate user account', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -108,6 +108,7 @@ describe('authenticate user account', () => { request: { protocol: 'https', get: (text) => text, + ip: '127.0.0.1', }, }, }) @@ -133,6 +134,99 @@ describe('authenticate user account', () => { }, }) }) + describe('user has not logged in in the past 30 days', () => { + it('returns sendMethod message and authentication token', async () => { + let cursor = await query` + FOR user IN users + FILTER user.userName == "test.account@istio.actually.exists" + RETURN MERGE({ id: user._key, _type: 'user' }, user) + ` + let user = await cursor.next() + + await query` + FOR user IN users + UPDATE ${user._key} WITH { tfaSendMethod: 'not_none', lastLogin: ${new Date( + new Date().setDate(new Date().getDate() - 31), + ).toISOString()} } IN users + ` + + const authToken = tokenize({ + expiresIn: AUTH_TOKEN_EXPIRY, + parameters: { userKey: user._key }, + secret: String(SIGN_IN_KEY), + }) + + const mockedTokenize = jest.fn().mockReturnValueOnce(authToken) + + const response = await graphql({ + schema, + source: ` + mutation { + signIn( + input: { + userName: "test.account@istio.actually.exists" + password: "testpassword123" + } + ) { + result { + ... on TFASignInResult { + authenticateToken + sendMethod + } + ... on AuthResult { + authToken + } + } + } + } + `, + rootValue: null, + contextValue: { + i18n, + query, + collections: collectionNames, + transaction, + uuidv4, + request: { ip: '127.0.0.1' }, + auth: { + bcrypt, + tokenize: mockedTokenize, + }, + validators: { + cleanseInput, + }, + loaders: { + loadUserByUserName: loadUserByUserName({ query }), + }, + notify: { + sendAuthEmail: mockNotify, + }, + }, + }) + + const expectedResponse = { + data: { + signIn: { + result: { + sendMethod: 'email', + authenticateToken: authToken, + }, + }, + }, + } + + cursor = await query` + FOR user IN users + FILTER user.userName == "test.account@istio.actually.exists" + RETURN MERGE({ id: user._key, _type: 'user' }, user) + ` + user = await cursor.next() + + expect(response).toEqual(expectedResponse) + expect(mockNotify).toHaveBeenCalledWith({ user }) + expect(consoleOutput).toEqual([`User: ${user._key} successfully signed in, and sent auth msg.`]) + }) + }) describe('user has send method set to phone', () => { it('returns sendMethod message, authentication token and refresh token', async () => { let cursor = await query` @@ -184,6 +278,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockedTokenize, @@ -276,6 +371,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockedTokenize, @@ -376,6 +472,7 @@ describe('authenticate user account', () => { transaction, response: mockedResponse, uuidv4, + request: { ip: '127.0.0.1' }, jwt, auth: { bcrypt, @@ -481,6 +578,7 @@ describe('authenticate user account', () => { transaction, response: mockedResponse, uuidv4, + request: { ip: '127.0.0.1' }, jwt, auth: { bcrypt, @@ -569,6 +667,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize, @@ -666,6 +765,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockedTokenize, @@ -760,6 +860,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockedTokenize, @@ -860,6 +961,7 @@ describe('authenticate user account', () => { transaction, response: mockedResponse, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockedTokenize, @@ -964,6 +1066,7 @@ describe('authenticate user account', () => { transaction, response: mockedResponse, uuidv4, + request: { ip: '127.0.0.1' }, jwt, auth: { bcrypt, @@ -1418,6 +1521,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -1489,6 +1593,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -1560,6 +1665,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -1632,6 +1738,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -1705,6 +1812,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('Transaction Commit Error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -1777,6 +1885,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('Transaction Commit Error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -1849,6 +1958,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('Transaction Commit Error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -2220,6 +2330,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -2291,6 +2402,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -2362,6 +2474,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -2434,6 +2547,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction Step Error')), commit: jest.fn(), + abort: jest.fn(), }), uuidv4, auth: { @@ -2507,6 +2621,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('Transaction Commit Error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -2579,6 +2694,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('Transaction Commit Error')), + abort: jest.fn(), }), uuidv4, auth: { @@ -2651,6 +2767,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn(), commit: jest.fn().mockRejectedValue(new Error('Transaction Commit Error')), + abort: jest.fn(), }), uuidv4, auth: { diff --git a/api/src/user/mutations/__tests__/sign-up.test.js b/api/src/user/mutations/__tests__/sign-up.test.js index 9262dd2be..7dd4b0c96 100644 --- a/api/src/user/mutations/__tests__/sign-up.test.js +++ b/api/src/user/mutations/__tests__/sign-up.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import bcrypt from 'bcryptjs' import { graphql, GraphQLError, GraphQLSchema } from 'graphql' import { setupI18n } from '@lingui/core' @@ -15,7 +16,7 @@ import { loadOrgByKey } from '../../../organization/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' -const { DB_PASS: rootPass, DB_URL: url, REFRESH_TOKEN_EXPIRY } = process.env +const { DB_PASS: rootPass, DB_URL: url, REFRESH_TOKEN_EXPIRY, TRACKER_PRODUCTION } = process.env describe('testing user sign up', () => { let query, drop, truncate, collections, transaction, schema, i18n, mockTokenize, mockNotify @@ -91,7 +92,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH } ) { result { @@ -113,6 +113,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -165,7 +166,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH } ) { result { @@ -187,6 +187,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -227,7 +228,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH rememberMe: true } ) { @@ -249,7 +249,7 @@ describe('testing user sign up', () => { query, collections: collectionNames, transaction, - + request: { ip: '127.0.0.1' }, uuidv4, auth: { bcrypt, @@ -304,7 +304,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH } ) { result { @@ -326,6 +325,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -402,7 +402,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -424,7 +423,7 @@ describe('testing user sign up', () => { query, collections: collectionNames, transaction, - + request: { ip: '127.0.0.1' }, uuidv4, auth: { bcrypt, @@ -480,7 +479,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -503,6 +501,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -555,7 +554,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -578,6 +576,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -620,7 +619,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" rememberMe: true } @@ -643,7 +641,7 @@ describe('testing user sign up', () => { query, collections: collectionNames, transaction, - + request: { ip: '127.0.0.1' }, uuidv4, auth: { bcrypt, @@ -699,7 +697,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -722,6 +719,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -774,7 +772,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -797,6 +794,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -859,7 +857,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -880,7 +877,7 @@ describe('testing user sign up', () => { query, collections: collectionNames, transaction, - + request: { ip: '127.0.0.1' }, uuidv4, auth: { bcrypt, @@ -933,7 +930,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH } ) { result { @@ -955,6 +951,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -995,7 +992,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH rememberMe: true } ) { @@ -1017,7 +1013,7 @@ describe('testing user sign up', () => { query, collections: collectionNames, transaction, - + request: { ip: '127.0.0.1' }, uuidv4, auth: { bcrypt, @@ -1071,7 +1067,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH } ) { result { @@ -1093,6 +1088,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1169,7 +1165,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" } ) { @@ -1193,6 +1188,7 @@ describe('testing user sign up', () => { transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1247,7 +1243,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -1270,6 +1265,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1322,7 +1318,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -1345,6 +1340,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1387,7 +1383,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" rememberMe: true } @@ -1412,6 +1407,7 @@ describe('testing user sign up', () => { transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1466,7 +1462,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -1489,6 +1484,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1541,7 +1537,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: ENGLISH signUpToken: "${token}" } ) { @@ -1564,6 +1559,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1626,7 +1622,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "123" confirmPassword: "123" - preferredLang: FRENCH } ) { result { @@ -1649,6 +1644,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1699,7 +1695,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "321drowssaptset" - preferredLang: FRENCH } ) { result { @@ -1722,6 +1717,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1772,7 +1768,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -1795,6 +1790,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1807,7 +1803,6 @@ describe('testing user sign up', () => { load: jest.fn().mockReturnValue({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneValidated: false, emailValidated: false, }), @@ -1862,7 +1857,6 @@ describe('testing user sign up', () => { userName: "test@email.ca" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" } ) { @@ -1888,8 +1882,10 @@ describe('testing user sign up', () => { step: jest.fn().mockReturnValue({ next: jest.fn(), }), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -1978,7 +1974,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" } ) { @@ -2004,8 +1999,10 @@ describe('testing user sign up', () => { step: jest.fn().mockReturnValue({ next: jest.fn(), }), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2062,7 +2059,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -2087,8 +2083,10 @@ describe('testing user sign up', () => { step: jest.fn().mockReturnValue({ next: jest.fn().mockRejectedValue('Cursor Error'), }), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2161,7 +2159,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -2184,8 +2181,10 @@ describe('testing user sign up', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue('Transaction Step Error'), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2266,7 +2265,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" } ) { @@ -2290,8 +2288,10 @@ describe('testing user sign up', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce({ next: jest.fn() }).mockRejectedValue('Transaction Step Error'), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2362,7 +2362,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -2386,8 +2385,10 @@ describe('testing user sign up', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({ next: jest.fn() }), commit: jest.fn().mockRejectedValue('Transaction Commit Error'), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2475,7 +2476,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "123" confirmPassword: "123" - preferredLang: FRENCH } ) { result { @@ -2498,6 +2498,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2548,7 +2549,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "321drowssaptset" - preferredLang: FRENCH } ) { result { @@ -2571,6 +2571,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2621,7 +2622,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -2644,6 +2644,7 @@ describe('testing user sign up', () => { collections: collectionNames, transaction, uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2656,7 +2657,6 @@ describe('testing user sign up', () => { load: jest.fn().mockReturnValue({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', phoneValidated: false, emailValidated: false, }), @@ -2711,7 +2711,6 @@ describe('testing user sign up', () => { userName: "test@email.ca" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" } ) { @@ -2737,8 +2736,10 @@ describe('testing user sign up', () => { step: jest.fn().mockReturnValue({ next: jest.fn(), }), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2828,7 +2829,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" } ) { @@ -2854,8 +2854,10 @@ describe('testing user sign up', () => { step: jest.fn().mockReturnValue({ next: jest.fn(), }), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -2913,7 +2915,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -2938,8 +2939,10 @@ describe('testing user sign up', () => { step: jest.fn().mockReturnValue({ next: jest.fn().mockRejectedValue('Cursor Error'), }), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -3012,7 +3015,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -3035,8 +3037,10 @@ describe('testing user sign up', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue('Transaction Step Error'), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -3117,7 +3121,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH signUpToken: "${token}" } ) { @@ -3141,8 +3144,10 @@ describe('testing user sign up', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValueOnce({ next: jest.fn() }).mockRejectedValue('Transaction Step Error'), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, @@ -3213,7 +3218,6 @@ describe('testing user sign up', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -3237,8 +3241,10 @@ describe('testing user sign up', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({ next: jest.fn() }), commit: jest.fn().mockRejectedValue('Transaction Commit Error'), + abort: jest.fn(), }), uuidv4, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize: mockTokenize, diff --git a/api/src/user/mutations/__tests__/update-user-password.test.js b/api/src/user/mutations/__tests__/update-user-password.test.js index 29c2c9105..b3910e594 100644 --- a/api/src/user/mutations/__tests__/update-user-password.test.js +++ b/api/src/user/mutations/__tests__/update-user-password.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import bcrypt from 'bcryptjs' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -66,7 +67,6 @@ describe('authenticate user account', () => { userName: "test.account@istio.actually.exists" password: "testpassword123" confirmPassword: "testpassword123" - preferredLang: FRENCH } ) { result { @@ -164,6 +164,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize, @@ -298,6 +299,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction, userKey: user._key, + request: { ip: '127.0.0.1' }, auth: { bcrypt, tokenize, @@ -628,6 +630,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), userKey: 123, auth: { @@ -689,6 +692,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), userKey: 123, auth: { @@ -960,6 +964,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), userKey: 123, auth: { @@ -1021,6 +1026,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), userKey: 123, auth: { diff --git a/api/src/user/mutations/__tests__/update-user-profile.test.js b/api/src/user/mutations/__tests__/update-user-profile.test.js index a74d8130b..ad8bfb23b 100644 --- a/api/src/user/mutations/__tests__/update-user-profile.test.js +++ b/api/src/user/mutations/__tests__/update-user-profile.test.js @@ -1,6 +1,7 @@ import bcrypt from 'bcryptjs' import crypto from 'crypto' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -14,7 +15,7 @@ import { loadUserByUserName, loadUserByKey } from '../../loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' -const { DB_PASS: rootPass, DB_URL: url, CIPHER_KEY } = process.env +const { DB_PASS: rootPass, DB_URL: url, CIPHER_KEY, AUTHENTICATED_KEY, AUTH_TOKEN_EXPIRY } = process.env describe('authenticate user account', () => { let query, drop, truncate, collections, transaction, schema, i18n @@ -57,7 +58,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'french', tfaSendMethod: 'none', }) }) @@ -155,7 +155,7 @@ describe('authenticate user account', () => { }) }) describe('user updates their user name', () => { - it('returns a successful status message, and the updated user info', async () => { + it('returns a successful status message and sends verify email link', async () => { const cursor = await query` FOR user IN users FILTER user.userName == "test.account@istio.actually.exists" @@ -163,12 +163,21 @@ describe('authenticate user account', () => { ` const user = await cursor.next() + const sendVerificationEmail = jest.fn() + const newUsername = 'john.doe@istio.actually.works' + + const verifyUrl = `https://domain.ca/validate/${tokenize({ + expiresIn: AUTH_TOKEN_EXPIRY, + parameters: { userKey: user._key, userName: newUsername }, + secret: String(AUTHENTICATED_KEY), + })}` + const response = await graphql({ schema, source: ` mutation { updateUserProfile( - input: { userName: "john.doe@istio.actually.works" } + input: { userName: "${newUsername}" } ) { result { ... on UpdateUserProfileResult { @@ -194,6 +203,7 @@ describe('authenticate user account', () => { userKey: user._key, request: { get: jest.fn().mockReturnValue('domain.ca'), + ip: '127.0.0.1', }, auth: { bcrypt, @@ -210,174 +220,33 @@ describe('authenticate user account', () => { loadUserByUserName: loadUserByUserName({ query }), loadUserByKey: loadUserByKey({ query }), }, - notify: { sendVerificationEmail: jest.fn() }, + notify: { sendVerificationEmail }, }, }) + // Does not change userName const expectedResponse = { data: { updateUserProfile: { result: { user: { - userName: 'john.doe@istio.actually.works', + userName: 'test.account@istio.actually.exists', }, status: 'Profile successfully updated.', }, }, }, } - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated their profile.`]) - }) - it('sends new verify email link', async () => { - const cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - const user = await cursor.next() - - const mockNotify = jest.fn() - - await graphql({ - schema, - source: ` - mutation { - updateUserProfile( - input: { userName: "john.doe@istio.actually.works" } - ) { - result { - ... on UpdateUserProfileResult { - status - user { - userName - } - } - ... on UpdateUserProfileError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { - get: jest.fn().mockReturnValue('domain.ca'), - }, - auth: { - bcrypt, - tokenize: jest.fn().mockReturnValue('token'), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - loadUserByKey: loadUserByKey({ query }), - }, - notify: { sendVerificationEmail: mockNotify }, - }, - }) - const updatedCursor = await query` - FOR user IN users - FILTER user.userName == "john.doe@istio.actually.works" - RETURN user - ` - const updatedUser = await updatedCursor.next() - - expect(mockNotify).toBeCalledWith({ - verifyUrl: 'https://domain.ca/validate/token', - user: { - id: updatedUser._key, - _type: 'user', - ...updatedUser, - }, + expect(sendVerificationEmail).toHaveBeenCalledWith({ + verifyUrl: verifyUrl, + userKey: user._key, + displayName: user.displayName, + userName: newUsername, }) - }) - describe('user is email validated', () => { - it('sets emailValidated to false', async () => { - const cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - UPDATE user._key WITH { - emailValidated: true, - } IN users - RETURN NEW - ` - const user = await cursor.next() - await graphql({ - schema, - source: ` - mutation { - updateUserProfile( - input: { userName: "john.doe@istio.actually.works" } - ) { - result { - ... on UpdateUserProfileResult { - status - user { - userName - } - } - ... on UpdateUserProfileError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { - get: jest.fn().mockReturnValue('domain.ca'), - }, - auth: { - bcrypt, - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - loadUserByKey: loadUserByKey({ query }), - }, - notify: { sendVerificationEmail: jest.fn() }, - }, - }) - - const checkCursor = await query` - FOR user IN users - RETURN user - ` - const checkUser = await checkCursor.next() - - expect(checkUser.emailValidated).toBeFalsy() - }) + expect(consoleOutput).toEqual([`User: ${user._key} successfully updated their profile.`]) }) describe('user is not email validated', () => { it('does not change emailValidated value', async () => { @@ -452,78 +321,6 @@ describe('authenticate user account', () => { }) }) }) - describe('user updates their preferred language', () => { - it('returns a successful status message, and the updated user info', async () => { - const cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - const user = await cursor.next() - - const response = await graphql({ - schema, - source: ` - mutation { - updateUserProfile(input: { preferredLang: ENGLISH }) { - result { - ... on UpdateUserProfileResult { - status - user { - preferredLang - } - } - ... on UpdateUserProfileError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - bcrypt, - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - loadUserByKey: loadUserByKey({ query }), - }, - notify: { sendVerificationEmail: jest.fn() }, - }, - }) - - const expectedResponse = { - data: { - updateUserProfile: { - result: { - user: { - preferredLang: 'ENGLISH', - }, - status: 'Profile successfully updated.', - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated their profile.`]) - }) - }) describe('user attempts to update their tfa send method', () => { describe('user attempts to set to phone', () => { describe('user is phone validated', () => { @@ -548,7 +345,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', phoneValidated: true, tfaSendMethod: 'none', phoneDetails: updatedPhoneDetails, @@ -647,7 +443,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', phoneValidated: false, tfaSendMethod: 'none', phoneDetails: updatedPhoneDetails, @@ -732,7 +527,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: true, tfaSendMethod: 'none', }) @@ -814,7 +608,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: false, tfaSendMethod: 'none', }) @@ -897,7 +690,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: true, tfaSendMethod: 'email', }) @@ -1064,36 +856,45 @@ describe('authenticate user account', () => { }) describe('user updates their user name', () => { describe('user updates their user name', () => { - it('returns a successful status message, and the updated user info', async () => { + it('returns a successful status message and sends verify email link', async () => { const cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` + FOR user IN users + FILTER user.userName == "test.account@istio.actually.exists" + RETURN user + ` const user = await cursor.next() + const sendVerificationEmail = jest.fn() + const newUsername = 'john.doe@istio.actually.works' + + const verifyUrl = `https://domain.ca/validate/${tokenize({ + expiresIn: AUTH_TOKEN_EXPIRY, + parameters: { userKey: user._key, userName: newUsername }, + secret: String(AUTHENTICATED_KEY), + })}` + const response = await graphql({ schema, source: ` - mutation { - updateUserProfile( - input: { userName: "john.doe@istio.actually.works" } - ) { - result { - ... on UpdateUserProfileResult { - status - user { - userName - } - } - ... on UpdateUserProfileError { - code - description + mutation { + updateUserProfile( + input: { userName: "${newUsername}" } + ) { + result { + ... on UpdateUserProfileResult { + status + user { + userName } } + ... on UpdateUserProfileError { + code + description + } } } - `, + } + `, rootValue: null, contextValue: { i18n, @@ -1103,6 +904,7 @@ describe('authenticate user account', () => { userKey: user._key, request: { get: jest.fn().mockReturnValue('domain.ca'), + ip: '127.0.0.1', }, auth: { bcrypt, @@ -1119,7 +921,7 @@ describe('authenticate user account', () => { loadUserByUserName: loadUserByUserName({ query }), loadUserByKey: loadUserByKey({ query }), }, - notify: { sendVerificationEmail: jest.fn() }, + notify: { sendVerificationEmail }, }, }) @@ -1128,166 +930,25 @@ describe('authenticate user account', () => { updateUserProfile: { result: { user: { - userName: 'john.doe@istio.actually.works', + userName: 'test.account@istio.actually.exists', }, status: 'Le profil a été mis à jour avec succès.', }, }, }, } - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated their profile.`]) - }) - it('sends new verify email link', async () => { - const cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - const user = await cursor.next() - const mockNotify = jest.fn() - - await graphql({ - schema, - source: ` - mutation { - updateUserProfile( - input: { userName: "john.doe@istio.actually.works" } - ) { - result { - ... on UpdateUserProfileResult { - status - user { - userName - } - } - ... on UpdateUserProfileError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { - get: jest.fn().mockReturnValue('domain.ca'), - }, - auth: { - bcrypt, - tokenize: jest.fn().mockReturnValue('token'), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - loadUserByKey: loadUserByKey({ query }), - }, - notify: { sendVerificationEmail: mockNotify }, - }, + expect(sendVerificationEmail).toHaveBeenCalledWith({ + verifyUrl: verifyUrl, + userKey: user._key, + displayName: user.displayName, + userName: newUsername, }) - const updatedCursor = await query` - FOR user IN users - FILTER user.userName == "john.doe@istio.actually.works" - RETURN user - ` - const updatedUser = await updatedCursor.next() - - expect(mockNotify).toBeCalledWith({ - verifyUrl: 'https://domain.ca/validate/token', - user: { - id: updatedUser._key, - _type: 'user', - ...updatedUser, - }, - }) + expect(consoleOutput).toEqual([`User: ${user._key} successfully updated their profile.`]) }) - describe('user is email validated', () => { - it('sets emailValidated to false', async () => { - const cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - UPDATE user._key WITH { - emailValidated: true, - } IN users - RETURN NEW - ` - const user = await cursor.next() - - await graphql({ - schema, - source: ` - mutation { - updateUserProfile( - input: { userName: "john.doe@istio.actually.works" } - ) { - result { - ... on UpdateUserProfileResult { - status - user { - userName - } - } - ... on UpdateUserProfileError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - request: { - get: jest.fn().mockReturnValue('domain.ca'), - }, - userKey: user._key, - auth: { - bcrypt, - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - loadUserByKey: loadUserByKey({ query }), - }, - notify: { sendVerificationEmail: jest.fn() }, - }, - }) - - const checkCursor = await query` - FOR user IN users - RETURN user - ` - const checkUser = await checkCursor.next() - expect(checkUser.emailValidated).toBeFalsy() - }) - }) describe('user is not email validated', () => { it('does not change emailValidated value', async () => { const cursor = await query` @@ -1362,78 +1023,6 @@ describe('authenticate user account', () => { }) }) }) - describe('user updates their preferred language', () => { - it('returns a successful status message, and the updated user info', async () => { - const cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - const user = await cursor.next() - - const response = await graphql({ - schema, - source: ` - mutation { - updateUserProfile(input: { preferredLang: ENGLISH }) { - result { - ... on UpdateUserProfileResult { - status - user { - preferredLang - } - } - ... on UpdateUserProfileError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: user._key, - auth: { - bcrypt, - tokenize, - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByUserName: loadUserByUserName({ query }), - loadUserByKey: loadUserByKey({ query }), - }, - notify: { sendVerificationEmail: jest.fn() }, - }, - }) - - const expectedResponse = { - data: { - updateUserProfile: { - result: { - user: { - preferredLang: 'ENGLISH', - }, - status: 'Le profil a été mis à jour avec succès.', - }, - }, - }, - } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key} successfully updated their profile.`]) - }) - }) describe('user attempts to update their tfa send method', () => { describe('user attempts to set to phone', () => { describe('user is phone validated', () => { @@ -1458,7 +1047,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', phoneValidated: true, tfaSendMethod: 'none', phoneDetails: updatedPhoneDetails, @@ -1557,7 +1145,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', phoneValidated: false, tfaSendMethod: 'none', phoneDetails: updatedPhoneDetails, @@ -1642,7 +1229,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: true, tfaSendMethod: 'none', }) @@ -1724,7 +1310,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: false, tfaSendMethod: 'none', }) @@ -1807,7 +1392,6 @@ describe('authenticate user account', () => { await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'english', emailValidated: true, tfaSendMethod: 'email', }) @@ -1983,7 +1567,6 @@ describe('authenticate user account', () => { input: { displayName: "John Smith" userName: "john.smith@istio.actually.works" - preferredLang: ENGLISH } ) { result { @@ -2008,6 +1591,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), userKey: 123, auth: { @@ -2052,7 +1636,6 @@ describe('authenticate user account', () => { input: { displayName: "John Smith" userName: "john.smith@istio.actually.works" - preferredLang: ENGLISH } ) { result { @@ -2078,6 +1661,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), userKey: 123, auth: { @@ -2208,7 +1792,6 @@ describe('authenticate user account', () => { input: { displayName: "John Smith" userName: "john.smith@istio.actually.works" - preferredLang: ENGLISH } ) { result { @@ -2233,6 +1816,7 @@ describe('authenticate user account', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), userKey: 123, auth: { @@ -2277,7 +1861,6 @@ describe('authenticate user account', () => { input: { displayName: "John Smith" userName: "john.smith@istio.actually.works" - preferredLang: ENGLISH } ) { result { @@ -2303,6 +1886,7 @@ describe('authenticate user account', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), userKey: 123, auth: { diff --git a/api/src/user/mutations/__tests__/verify-account.test.js b/api/src/user/mutations/__tests__/verify-account.test.js index 3f1ac533d..ec71acb6c 100644 --- a/api/src/user/mutations/__tests__/verify-account.test.js +++ b/api/src/user/mutations/__tests__/verify-account.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -8,7 +9,7 @@ import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { tokenize, verifyToken } from '../../../auth' -import { loadUserByKey } from '../../loaders' +import { loadUserByKey, loadUserByUserName } from '../../loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -21,7 +22,7 @@ describe('user send password reset email', () => { const mockedWarn = (output) => consoleOutput.push(output) const mockedError = (output) => consoleOutput.push(output) - beforeAll(() => { + beforeAll(async () => { console.info = mockedInfo console.warn = mockedWarn console.error = mockedError @@ -33,31 +34,27 @@ describe('user send password reset email', () => { protocol: 'https', get: (text) => text, } + ;({ query, drop, truncate, collections, transaction } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) - afterEach(() => { + afterEach(async () => { consoleOutput.length = 0 + await truncate() + }) + afterAll(async () => { + await drop() }) describe('given a successful validation', () => { - beforeAll(async () => { - ;({ query, drop, truncate, collections, transaction } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - }) - afterEach(async () => { - await truncate() - }) - afterAll(async () => { - await drop() - }) describe('users language is set to english', () => { beforeAll(() => { i18n = setupI18n({ @@ -77,20 +74,24 @@ describe('user send password reset email', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }) }) - it('returns a successful status message', async () => { + it('returns a successful status message and update username', async () => { + const previousUserName = 'test.account@istio.actually.exists' let cursor = await query` FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" + FILTER user.userName == ${previousUserName} RETURN user ` let user = await cursor.next() - const token = tokenize({ parameters: { userKey: user._key } }) + const newUserName = 'john.doe@istio.actually.works' + + const token = tokenize({ parameters: { userKey: user._key, userName: newUserName } }) + + const sendUpdatedUserNameEmail = jest.fn() const response = await graphql({ schema, @@ -125,7 +126,9 @@ describe('user send password reset email', () => { }, loaders: { loadUserByKey: loadUserByKey({ query }), + loadUserByUserName: loadUserByUserName({ query }), }, + notify: { sendUpdatedUserNameEmail }, }, }) @@ -133,228 +136,31 @@ describe('user send password reset email', () => { data: { verifyAccount: { result: { - status: 'Successfully email verified account, and set TFA send method to email.', + status: 'Successfully email verified account.', }, }, }, } - cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - user = await cursor.next() - - expect(response).toEqual(expectedResult) - expect(consoleOutput).toEqual([`User: ${user._key} successfully email validated their account.`]) - }) - it('sets emailValidated to true', async () => { - let cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - let user = await cursor.next() - - const token = tokenize({ parameters: { userKey: user._key } }) - - await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: user._key, - query, - collections: collectionNames, - transaction, - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: loadUserByKey({ query }), - }, - }, + expect(sendUpdatedUserNameEmail).toHaveBeenCalledWith({ + previousUserName: previousUserName, + newUserName: newUserName, + displayName: user.displayName, + userKey: user._key, }) cursor = await query` FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" + FILTER user.userName == ${newUserName} RETURN user ` user = await cursor.next() expect(user.emailValidated).toEqual(true) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - beforeEach(async () => { - await collections.users.save({ - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'english', - tfaValidated: false, - emailValidated: false, - }) - }) - it('returns a successful status message', async () => { - let cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - let user = await cursor.next() - - const token = tokenize({ parameters: { userKey: user._key } }) - - const response = await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: user._key, - query, - collections: collectionNames, - transaction, - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResult = { - data: { - verifyAccount: { - result: { - status: - "Réussir à envoyer un email au compte vérifié, et définir la méthode d'envoi de la TFA sur email.", - }, - }, - }, - } - - cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - user = await cursor.next() expect(response).toEqual(expectedResult) - expect(user.emailValidated).toEqual(true) expect(consoleOutput).toEqual([`User: ${user._key} successfully email validated their account.`]) }) - it('sets emailValidated to true', async () => { - let cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - let user = await cursor.next() - - const token = tokenize({ parameters: { userKey: user._key } }) - - await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: user._key, - query, - collections: collectionNames, - transaction, - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - cursor = await query` - FOR user IN users - FILTER user.userName == "test.account@istio.actually.exists" - RETURN user - ` - user = await cursor.next() - - expect(user.emailValidated).toEqual(true) - }) }) }) describe('given an unsuccessful validation', () => { @@ -415,12 +221,13 @@ describe('user send password reset email', () => { load: jest.fn().mockReturnValue({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }), }, + loadUserByUserName: loadUserByUserName({ query }), }, + notify: { sendUpdatedUserNameEmail: jest.fn() }, }, }) @@ -483,12 +290,13 @@ describe('user send password reset email', () => { load: jest.fn().mockReturnValue({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }), }, + loadUserByUserName: loadUserByUserName({ query }), }, + notify: { sendUpdatedUserNameEmail: jest.fn() }, }, }) @@ -512,7 +320,7 @@ describe('user send password reset email', () => { describe('user cannot be found in db', () => { it('returns an error message', async () => { const token = tokenize({ - parameters: { userKey: 1 }, + parameters: { userKey: 1, userName: 'john.doe@istio.actually.exists' }, }) const response = await graphql({ schema, @@ -549,7 +357,9 @@ describe('user send password reset email', () => { loadUserByKey: { load: jest.fn().mockReturnValue(undefined), }, + loadUserByUserName: loadUserByUserName({ query }), }, + notify: { sendUpdatedUserNameEmail: jest.fn() }, }, }) @@ -574,7 +384,7 @@ describe('user send password reset email', () => { describe('when upserting validation', () => { it('throws an error', async () => { const token = tokenize({ - parameters: { userKey: 123 }, + parameters: { userKey: 123, userName: 'john.doe@istio.actually.exists' }, }) const response = await graphql({ @@ -603,6 +413,7 @@ describe('user send password reset email', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error occurred.')), + abort: jest.fn(), }), auth: { verifyToken: verifyToken({}), @@ -616,12 +427,13 @@ describe('user send password reset email', () => { _key: 123, userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }), }, + loadUserByUserName: loadUserByUserName({ query, userKey: 123, i18n }), }, + notify: { sendUpdatedUserNameEmail: jest.fn() }, }, }) @@ -638,7 +450,7 @@ describe('user send password reset email', () => { describe('when upserting validation', () => { it('throws an error', async () => { const token = tokenize({ - parameters: { userKey: 123 }, + parameters: { userKey: 123, userName: 'john.doe@istio.actually.exists' }, }) const response = await graphql({ @@ -668,6 +480,7 @@ describe('user send password reset email', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error occurred.')), + abort: jest.fn(), }), auth: { verifyToken: verifyToken({}), @@ -681,359 +494,18 @@ describe('user send password reset email', () => { _key: 123, userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'english', tfaValidated: false, emailValidated: false, }), }, + loadUserByUserName: loadUserByUserName({ query, userKey: 123, i18n }), }, + notify: { sendUpdatedUserNameEmail: jest.fn() }, }, }) const error = [new GraphQLError('Unable to verify account. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx commit error occurred when upserting email validation for user: 123: Error: Transaction commit error occurred.`, - ]) - }) - }) - }) - }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('userKey cannot be found in token parameters', () => { - it('returns an error message', async () => { - const token = tokenize({ - parameters: {}, - }) - - const response = await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: 123, - query, - collections: collectionNames, - transaction, - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: { - load: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'english', - tfaValidated: false, - emailValidated: false, - }), - }, - }, - }, - }) - - const error = { - data: { - verifyAccount: { - result: { - code: 400, - description: 'Impossible de vérifier le compte. Veuillez demander un nouvel e-mail.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `When validating account, user attempted to verify account, but userKey is not located in the token parameters.`, - ]) - }) - }) - describe('userKey in token is undefined', () => { - it('returns an error message', async () => { - const token = tokenize({ - parameters: { userKey: undefined }, - }) - - const response = await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: 123, - query, - collections: collectionNames, - transaction, - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: { - load: jest.fn().mockReturnValue({ - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'english', - tfaValidated: false, - emailValidated: false, - }), - }, - }, - }, - }) - - const error = { - data: { - verifyAccount: { - result: { - code: 400, - description: 'Impossible de vérifier le compte. Veuillez demander un nouvel e-mail.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `When validating account, user attempted to verify account, but userKey is not located in the token parameters.`, - ]) - }) - }) - describe('user cannot be found in db', () => { - it('returns an error message', async () => { - const token = tokenize({ - parameters: { userKey: 1 }, - }) - const response = await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: 1, - query, - collections: collectionNames, - transaction, - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - }, - }, - }) - - const error = { - data: { - verifyAccount: { - result: { - code: 400, - description: 'Impossible de vérifier le compte. Veuillez demander un nouvel e-mail.', - }, - }, - }, - } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 1 attempted to verify account, however no account is associated with this id.`, - ]) - }) - }) - describe('transaction step error occurs', () => { - describe('when upserting validation', () => { - it('throws an error', async () => { - const token = tokenize({ - parameters: { userKey: 123 }, - }) - - const response = await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: 123, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('Transaction step error occurred.')), - }), - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'english', - tfaValidated: false, - emailValidated: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Impossible de vérifier le compte. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred when upserting email validation for user: 123: Error: Transaction step error occurred.`, - ]) - }) - }) - }) - describe('transaction commit error occurs', () => { - describe('when upserting validation', () => { - it('throws an error', async () => { - const token = tokenize({ - parameters: { userKey: 123 }, - }) - - const response = await graphql({ - schema, - source: ` - mutation { - verifyAccount(input: { verifyTokenString: "${token}" }) { - result { - ... on VerifyAccountResult { - status - } - ... on VerifyAccountError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request, - userKey: 123, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({}), - commit: jest.fn().mockRejectedValue(new Error('Transaction commit error occurred.')), - }), - auth: { - verifyToken: verifyToken({}), - }, - validators: { - cleanseInput, - }, - loaders: { - loadUserByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - userName: 'test.account@istio.actually.exists', - displayName: 'Test Account', - preferredLang: 'english', - tfaValidated: false, - emailValidated: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Impossible de vérifier le compte. Veuillez réessayer.')] - expect(response.errors).toEqual(error) expect(consoleOutput).toEqual([ `Trx commit error occurred when upserting email validation for user: 123: Error: Transaction commit error occurred.`, diff --git a/api/src/user/mutations/__tests__/verify-phone-number.test.js b/api/src/user/mutations/__tests__/verify-phone-number.test.js index 649ab30d5..4da0f61bf 100644 --- a/api/src/user/mutations/__tests__/verify-phone-number.test.js +++ b/api/src/user/mutations/__tests__/verify-phone-number.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -51,7 +52,6 @@ describe('user send password reset email', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, tfaCode: 123456, @@ -475,6 +475,7 @@ describe('user send password reset email', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), auth: { userRequired: jest.fn().mockReturnValue({ @@ -531,6 +532,7 @@ describe('user send password reset email', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), auth: { userRequired: jest.fn().mockReturnValue({ @@ -721,6 +723,7 @@ describe('user send password reset email', () => { collections: collectionNames, transaction: jest.fn().mockReturnValue({ step: jest.fn().mockRejectedValue(new Error('Transaction step error')), + abort: jest.fn(), }), auth: { userRequired: jest.fn().mockReturnValue({ @@ -777,6 +780,7 @@ describe('user send password reset email', () => { transaction: jest.fn().mockReturnValue({ step: jest.fn().mockReturnValue({}), commit: jest.fn().mockRejectedValue(new Error('Transaction commit error')), + abort: jest.fn(), }), auth: { userRequired: jest.fn().mockReturnValue({ diff --git a/api/src/user/mutations/authenticate.js b/api/src/user/mutations/authenticate.js index 40c548dce..1761186ff 100644 --- a/api/src/user/mutations/authenticate.js +++ b/api/src/user/mutations/authenticate.js @@ -83,6 +83,7 @@ export const authenticate = new mutationWithClientMutationId({ // Check to see if security token matches the user submitted one if (authenticationCode === user.tfaCode) { const refreshId = uuidv4() + const loginDate = new Date().toISOString() const refreshInfo = { refreshId, @@ -101,11 +102,13 @@ export const authenticate = new mutationWithClientMutationId({ UPSERT { _key: ${user._key} } INSERT { tfaCode: null, - refreshInfo: ${refreshInfo} + refreshInfo: ${refreshInfo}, + lastLogin: ${loginDate} } UPDATE { tfaCode: null, - refreshInfo: ${refreshInfo} + refreshInfo: ${refreshInfo}, + lastLogin: ${loginDate} } IN users `, @@ -114,6 +117,7 @@ export const authenticate = new mutationWithClientMutationId({ console.error( `Trx step error occurred when clearing tfa code and setting refresh id for user: ${user._key} during authentication: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to authenticate. Please try again.`)) } @@ -138,6 +142,7 @@ export const authenticate = new mutationWithClientMutationId({ console.error( `Trx step error occurred when setting email validated to true for user: ${user._key} during authentication: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to authenticate. Please try again.`)) } } @@ -146,6 +151,7 @@ export const authenticate = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred while user: ${user._key} attempted to authenticate: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to authenticate. Please try again.`)) } @@ -211,6 +217,7 @@ export const authenticate = new mutationWithClientMutationId({ console.error( `Trx step error occurred when clearing tfa code on attempt timeout for user: ${user._key} during authentication: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Incorrect TFA code. Please sign in again.`)) } @@ -218,6 +225,7 @@ export const authenticate = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred while user: ${user._key} attempted to authenticate: ${err}`) + await trx.abort() throw new Error(i18n._(t`Incorrect TFA code. Please sign in again.`)) } throw new Error(i18n._(t`Incorrect TFA code. Please sign in again.`)) diff --git a/api/src/user/mutations/close-account.js b/api/src/user/mutations/close-account.js index 9f6d8b244..7a03baa58 100644 --- a/api/src/user/mutations/close-account.js +++ b/api/src/user/mutations/close-account.js @@ -17,7 +17,7 @@ export const closeAccountSelf = new mutationWithClientMutationId({ }), mutateAndGetPayload: async ( args, - { i18n, query, collections, transaction, auth: { userRequired }, validators: { cleanseInput } }, + { i18n, query, collections, transaction, request: { ip }, auth: { userRequired }, validators: { cleanseInput } }, ) => { let submittedUserId if (args?.userId) { @@ -45,6 +45,7 @@ export const closeAccountSelf = new mutationWithClientMutationId({ console.error( `Trx step error occurred when removing users remaining affiliations when user: ${user._key} attempted to close account: ${userId}: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to close account. Please try again.`)) } @@ -60,6 +61,7 @@ export const closeAccountSelf = new mutationWithClientMutationId({ console.error( `Trx step error occurred when removing user: ${user._key} attempted to close account: ${userId}: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to close account. Please try again.`)) } @@ -67,6 +69,7 @@ export const closeAccountSelf = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred when user: ${user._key} attempted to close account: ${userId}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to close account. Please try again.`)) } @@ -79,6 +82,7 @@ export const closeAccountSelf = new mutationWithClientMutationId({ id: user._key, userName: user.userName, role: submittedUserId ? 'SUPER_ADMIN' : '', + ipAddress: ip, }, action: 'delete', target: { @@ -117,6 +121,7 @@ export const closeAccountOther = new mutationWithClientMutationId({ query, collections, transaction, + request: { ip }, auth: { checkSuperAdmin, userRequired }, loaders: { loadUserByKey }, validators: { cleanseInput }, @@ -174,6 +179,7 @@ export const closeAccountOther = new mutationWithClientMutationId({ console.error( `Trx step error occurred when removing users remaining affiliations when user: ${user._key} attempted to close account: ${userId}: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to close account. Please try again.`)) } @@ -189,6 +195,7 @@ export const closeAccountOther = new mutationWithClientMutationId({ console.error( `Trx step error occurred when removing user: ${user._key} attempted to close account: ${userId}: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to close account. Please try again.`)) } @@ -196,6 +203,7 @@ export const closeAccountOther = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred when user: ${user._key} attempted to close account: ${userId}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to close account. Please try again.`)) } @@ -208,6 +216,7 @@ export const closeAccountOther = new mutationWithClientMutationId({ id: user._key, userName: user.userName, role: submittedUserId ? 'SUPER_ADMIN' : '', + ipAddress: ip, }, action: 'delete', target: { @@ -219,6 +228,7 @@ export const closeAccountOther = new mutationWithClientMutationId({ return { _type: 'regular', status: i18n._(t`Successfully closed account.`), + user: checkUser, } }, }) diff --git a/api/src/user/mutations/complete-tour.js b/api/src/user/mutations/complete-tour.js new file mode 100644 index 000000000..8fff7a945 --- /dev/null +++ b/api/src/user/mutations/complete-tour.js @@ -0,0 +1,77 @@ +import { GraphQLString } from 'graphql' +import { mutationWithClientMutationId } from 'graphql-relay' +import { t } from '@lingui/macro' + +import { completeTourUnion } from '../unions' + +export const completeTour = new mutationWithClientMutationId({ + name: 'CompleteTour', + description: + 'This mutation allows users to confirm that they have completed the tour. This mutation will update the user object in the database to reflect that the user has completed the tour.', + inputFields: () => ({ + tourId: { + type: GraphQLString, + description: 'The id of the tour that the user is confirming completion of.', + }, + }), + outputFields: () => ({ + result: { + type: completeTourUnion, + description: '`CompleteTourUnion` returning either a `CompleteTourResult`, or `CompleteTourError` object.', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { i18n, query, auth: { userRequired }, loaders: { loadUserByKey }, validators: { cleanseInput } }, + ) => { + // Cleanse Input + const tourId = cleanseInput(args.tourId) + + // Get user info from DB + const user = await userRequired() + + if (!tourId) { + console.warn(`User: ${user._key} did not provide a tour id when attempting to confirm completion of the tour.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to confirm completion of the tour. Please try again.`), + } + } + + // Complete tour + try { + const completeTourCursor = await query` + LET userCompleteTours = FIRST( + FOR user IN users + FILTER user._key == ${user._key} + LIMIT 1 + RETURN user.completedTours + ) + UPDATE { _key: ${user._key} } + WITH { + completedTours: APPEND( + userCompleteTours[* FILTER CURRENT.tourId != ${tourId}], + { tourId: ${tourId}, completedAt: DATE_ISO8601(DATE_NOW()) } + ) + } + IN users + ` + await completeTourCursor.next() + } catch (err) { + console.error(`Database error occurred when user: ${user._key} attempted to complete tour: ${tourId}: ${err}`) + throw new Error(i18n._(t`Unable to confirm completion of the tour. Please try again.`)) + } + + await loadUserByKey.clear(user._key) + const returnUser = await loadUserByKey.load(user._key) + + console.info(`User: ${user._key} has confirmed completion of tour: ${tourId}`) + return { + _type: 'success', + status: i18n._(t`Tour completion confirmed successfully`), + user: returnUser, + } + }, +}) diff --git a/api/src/user/mutations/dismiss-message.js b/api/src/user/mutations/dismiss-message.js new file mode 100644 index 000000000..a5bdbc387 --- /dev/null +++ b/api/src/user/mutations/dismiss-message.js @@ -0,0 +1,79 @@ +import { GraphQLString } from 'graphql' +import { mutationWithClientMutationId } from 'graphql-relay' +import { t } from '@lingui/macro' + +import { dismissMessageUnion } from '../unions' + +export const dismissMessage = new mutationWithClientMutationId({ + name: 'DismissMessage', + description: + 'This mutation allows users to dismiss a message that is displayed to them when they log in. This mutation will update the user object in the database to reflect that the message has been dismissed.', + inputFields: () => ({ + messageId: { + type: GraphQLString, + description: 'The id of the message that the user is dismissing.', + }, + }), + outputFields: () => ({ + result: { + type: dismissMessageUnion, + description: '`DismissMessageUnion` returning either a `DismissMessageResult`, or `DismissMessageError` object.', + resolve: (payload) => payload, + }, + }), + mutateAndGetPayload: async ( + args, + { i18n, query, auth: { userRequired }, loaders: { loadUserByKey }, validators: { cleanseInput } }, + ) => { + // Cleanse Input + const messageId = cleanseInput(args.messageId) + + // Get user info from DB + const user = await userRequired() + + if (!messageId) { + console.warn(`User: ${user._key} did not provide a message id when attempting to dismiss a message.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to dismiss message. Please try again.`), + } + } + + // Dismiss message + try { + const dismissMessageCursor = await query` + LET userDismissedMessages = FIRST( + FOR user IN users + FILTER user._key == ${user._key} + LIMIT 1 + RETURN user.dismissedMessages + ) + UPDATE { _key: ${user._key} } + WITH { + dismissedMessages: APPEND( + userDismissedMessages[* FILTER CURRENT.messageId != ${messageId}], + { messageId: ${messageId}, dismissedAt: DATE_ISO8601(DATE_NOW()) } + ) + } + IN users + ` + await dismissMessageCursor.next() + } catch (err) { + console.error( + `Database error occurred when user: ${user._key} attempted to dismiss message: ${messageId}: ${err}`, + ) + throw new Error(i18n._(t`Unable to dismiss message. Please try again.`)) + } + + await loadUserByKey.clear(user._key) + const returnUser = await loadUserByKey.load(user._key) + + console.info(`User: ${user._key} successfully dismissed message: ${messageId}`) + return { + _type: 'success', + status: i18n._(t`Message dismissed successfully`), + user: returnUser, + } + }, +}) diff --git a/api/src/user/mutations/index.js b/api/src/user/mutations/index.js index 372334dd7..0a11011ec 100644 --- a/api/src/user/mutations/index.js +++ b/api/src/user/mutations/index.js @@ -1,9 +1,10 @@ export * from './authenticate' export * from './close-account' +export * from './complete-tour' +export * from './dismiss-message' export * from './refresh-tokens' export * from './remove-phone-number' export * from './reset-password' -export * from './send-email-verification' export * from './send-password-reset' export * from './set-phone-number' export * from './sign-in' diff --git a/api/src/user/mutations/refresh-tokens.js b/api/src/user/mutations/refresh-tokens.js index 8deda5b2a..0028b753e 100644 --- a/api/src/user/mutations/refresh-tokens.js +++ b/api/src/user/mutations/refresh-tokens.js @@ -119,6 +119,7 @@ export const refreshTokens = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred when attempting to refresh tokens for user: ${userKey}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to refresh tokens, please sign in.`)) } @@ -126,6 +127,7 @@ export const refreshTokens = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred while user: ${userKey} attempted to refresh tokens: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to refresh tokens, please sign in.`)) } diff --git a/api/src/user/mutations/remove-phone-number.js b/api/src/user/mutations/remove-phone-number.js index 92b6ff4c4..b1d449b1a 100644 --- a/api/src/user/mutations/remove-phone-number.js +++ b/api/src/user/mutations/remove-phone-number.js @@ -1,12 +1,11 @@ -import {t} from '@lingui/macro' -import {mutationWithClientMutationId} from 'graphql-relay' +import { t } from '@lingui/macro' +import { mutationWithClientMutationId } from 'graphql-relay' -import {removePhoneNumberUnion} from '../unions' +import { removePhoneNumberUnion } from '../unions' export const removePhoneNumber = new mutationWithClientMutationId({ name: 'RemovePhoneNumber', - description: - 'This mutation allows for users to remove a phone number from their account.', + description: 'This mutation allows for users to remove a phone number from their account.', outputFields: () => ({ result: { type: removePhoneNumberUnion, @@ -15,10 +14,7 @@ export const removePhoneNumber = new mutationWithClientMutationId({ resolve: (payload) => payload, }, }), - mutateAndGetPayload: async ( - _args, - {i18n, collections, query, transaction, auth: {userRequired}}, - ) => { + mutateAndGetPayload: async (_args, { i18n, collections, query, transaction, auth: { userRequired } }) => { // Get requesting user const user = await userRequired() @@ -50,23 +46,17 @@ export const removePhoneNumber = new mutationWithClientMutationId({ `, ) } catch (err) { - console.error( - `Trx step error occurred well removing phone number for user: ${user._key}: ${err}`, - ) - throw new Error( - i18n._(t`Unable to remove phone number. Please try again.`), - ) + console.error(`Trx step error occurred well removing phone number for user: ${user._key}: ${err}`) + await trx.abort() + throw new Error(i18n._(t`Unable to remove phone number. Please try again.`)) } try { await trx.commit() } catch (err) { - console.error( - `Trx commit error occurred well removing phone number for user: ${user._key}: ${err}`, - ) - throw new Error( - i18n._(t`Unable to remove phone number. Please try again.`), - ) + console.error(`Trx commit error occurred well removing phone number for user: ${user._key}: ${err}`) + await trx.abort() + throw new Error(i18n._(t`Unable to remove phone number. Please try again.`)) } console.info(`User: ${user._key} successfully removed their phone number.`) diff --git a/api/src/user/mutations/reset-password.js b/api/src/user/mutations/reset-password.js index b594ebb12..8deb0bbb7 100644 --- a/api/src/user/mutations/reset-password.js +++ b/api/src/user/mutations/reset-password.js @@ -121,6 +121,7 @@ export const resetPassword = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred when user: ${user._key} attempted to reset their password: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to reset password. Please try again.`)) } @@ -128,6 +129,7 @@ export const resetPassword = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred while user: ${user._key} attempted to authenticate: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to reset password. Please try again.`)) } diff --git a/api/src/user/mutations/send-email-verification.js b/api/src/user/mutations/send-email-verification.js deleted file mode 100644 index 95e79b41f..000000000 --- a/api/src/user/mutations/send-email-verification.js +++ /dev/null @@ -1,64 +0,0 @@ -import { GraphQLString, GraphQLNonNull } from 'graphql' -import { mutationWithClientMutationId } from 'graphql-relay' -import { GraphQLEmailAddress } from 'graphql-scalars' -import { t } from '@lingui/macro' - -export const sendEmailVerification = new mutationWithClientMutationId({ - name: 'SendEmailVerification', - description: 'This mutation is used for re-sending a verification email if it failed during user creation.', - inputFields: () => ({ - userName: { - type: new GraphQLNonNull(GraphQLEmailAddress), - description: 'The users email address used for sending the verification email.', - }, - }), - outputFields: () => ({ - status: { - type: GraphQLString, - description: 'Informs the user if the email was sent successfully.', - resolve: async (payload) => { - return payload.status - }, - }, - }), - mutateAndGetPayload: async ( - args, - { - i18n, - request, - auth: { tokenize }, - validators: { cleanseInput }, - loaders: { loadUserByUserName }, - notify: { sendVerificationEmail }, - }, - ) => { - // Cleanse Input - const userName = cleanseInput(args.userName).toLowerCase() - - // Get user from db - const user = await loadUserByUserName.load(userName) - - if (typeof user !== 'undefined') { - const token = tokenize({ - expiresIn: '1h', - parameters: { userKey: user._key }, - }) - - const verifyUrl = `https://${request.get('host')}/validate/${token}` - - await sendVerificationEmail({ user, verifyUrl }) - - console.info(`User: ${user._key} successfully sent a verification email.`) - } else { - console.warn( - `A user attempted to send a verification email for ${userName} but no account is affiliated with this user name.`, - ) - } - - return { - status: i18n._( - t`If an account with this username is found, an email verification link will be found in your inbox.`, - ), - } - }, -}) diff --git a/api/src/user/mutations/set-phone-number.js b/api/src/user/mutations/set-phone-number.js index c35a959e2..09f874d6e 100644 --- a/api/src/user/mutations/set-phone-number.js +++ b/api/src/user/mutations/set-phone-number.js @@ -35,7 +35,7 @@ export const setPhoneNumber = new mutationWithClientMutationId({ auth: { userRequired }, loaders: { loadUserByKey }, validators: { cleanseInput }, - notify: { sendTfaTextMsg }, + notify: { sendAuthTextMsg }, }, ) => { // Cleanse input @@ -93,6 +93,7 @@ export const setPhoneNumber = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred for user: ${user._key} when upserting phone number information: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to set phone number, please try again.`)) } @@ -100,6 +101,7 @@ export const setPhoneNumber = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred for user: ${user._key} when upserting phone number information: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to set phone number, please try again.`)) } @@ -107,7 +109,7 @@ export const setPhoneNumber = new mutationWithClientMutationId({ await loadUserByKey.clear(user._key) user = await loadUserByKey.load(user._key) - await sendTfaTextMsg({ phoneNumber, user }) + await sendAuthTextMsg({ user }) console.info(`User: ${user._key} successfully set phone number.`) return { diff --git a/api/src/user/mutations/sign-in.js b/api/src/user/mutations/sign-in.js index d95d5ae54..bf0efdcdd 100644 --- a/api/src/user/mutations/sign-in.js +++ b/api/src/user/mutations/sign-in.js @@ -94,6 +94,7 @@ export const signIn = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred when resetting failed login attempts for user: ${user._key}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to sign in, please try again.`)) } @@ -127,6 +128,7 @@ export const signIn = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred when inserting TFA code for user: ${user._key}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to sign in, please try again.`)) } @@ -134,6 +136,7 @@ export const signIn = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred while user: ${user._key} attempted to tfa sign in: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to sign in, please try again.`)) } @@ -141,14 +144,25 @@ export const signIn = new mutationWithClientMutationId({ await loadUserByUserName.clear(userName) user = await loadUserByUserName.load(userName) + // Check if user's last successful login was over 30 days ago + let lastLogin + if (user.lastLogin) { + lastLogin = new Date(user.lastLogin) + } else { + lastLogin = new Date() + } + const currentDate = new Date() + const timeDifference = currentDate - lastLogin + const daysDifference = timeDifference / (1000 * 3600 * 24) + // Check to see if user has phone validated let sendMethod - if (user.tfaSendMethod === 'phone') { - await sendAuthTextMsg({ user }) - sendMethod = 'text' - } else { + if (user.tfaSendMethod === 'email' || daysDifference >= 30) { await sendAuthEmail({ user }) sendMethod = 'email' + } else { + await sendAuthTextMsg({ user }) + sendMethod = 'text' } console.info(`User: ${user._key} successfully signed in, and sent auth msg.`) @@ -165,13 +179,14 @@ export const signIn = new mutationWithClientMutationId({ authenticateToken, } } else { + const loginDate = new Date().toISOString() try { await trx.step( () => query` WITH users UPSERT { _key: ${user._key} } - INSERT { refreshInfo: ${refreshInfo} } - UPDATE { refreshInfo: ${refreshInfo} } + INSERT { refreshInfo: ${refreshInfo}, lastLogin: ${loginDate} } + UPDATE { refreshInfo: ${refreshInfo}, lastLogin: ${loginDate} } IN users `, ) @@ -179,6 +194,7 @@ export const signIn = new mutationWithClientMutationId({ console.error( `Trx step error occurred when attempting to setting refresh tokens for user: ${user._key} during sign in: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to sign in, please try again.`)) } @@ -186,6 +202,7 @@ export const signIn = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred while user: ${user._key} attempted a regular sign in: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to sign in, please try again.`)) } @@ -249,6 +266,7 @@ export const signIn = new mutationWithClientMutationId({ console.error( `Trx step error occurred when incrementing failed login attempts for user: ${user._key}: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to sign in, please try again.`)) } @@ -256,6 +274,7 @@ export const signIn = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred while user: ${user._key} failed to sign in: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to sign in, please try again.`)) } diff --git a/api/src/user/mutations/sign-up.js b/api/src/user/mutations/sign-up.js index a19c66763..16dfb0eed 100644 --- a/api/src/user/mutations/sign-up.js +++ b/api/src/user/mutations/sign-up.js @@ -3,12 +3,12 @@ import { mutationWithClientMutationId } from 'graphql-relay' import { t } from '@lingui/macro' import { GraphQLEmailAddress } from 'graphql-scalars' -import { LanguageEnums } from '../../enums' import { signUpUnion } from '../unions' import { logActivity } from '../../audit-logs/mutations/log-activity' import ms from 'ms' +import { emailUpdateOptionsType } from '../objects/email-update-options' -const { REFRESH_TOKEN_EXPIRY, SIGN_IN_KEY, AUTH_TOKEN_EXPIRY } = process.env +const { REFRESH_TOKEN_EXPIRY, SIGN_IN_KEY, AUTH_TOKEN_EXPIRY, TRACKER_PRODUCTION } = process.env export const signUp = new mutationWithClientMutationId({ name: 'SignUp', @@ -30,10 +30,6 @@ export const signUp = new mutationWithClientMutationId({ type: new GraphQLNonNull(GraphQLString), description: 'A secondary password field used to confirm the user entered the correct password.', }, - preferredLang: { - type: new GraphQLNonNull(LanguageEnums), - description: 'The users preferred language.', - }, signUpToken: { type: GraphQLString, description: 'A token sent by email, that will assign a user to an organization with a pre-determined role.', @@ -59,6 +55,7 @@ export const signUp = new mutationWithClientMutationId({ query, transaction, uuidv4, + request: { ip }, auth: { bcrypt, tokenize, verifyToken }, loaders: { loadOrgByKey, loadUserByUserName, loadUserByKey }, notify: { sendAuthEmail }, @@ -70,10 +67,19 @@ export const signUp = new mutationWithClientMutationId({ const userName = cleanseInput(args.userName).toLowerCase() const password = cleanseInput(args.password) const confirmPassword = cleanseInput(args.confirmPassword) - const preferredLang = cleanseInput(args.preferredLang) const signUpToken = cleanseInput(args.signUpToken) const rememberMe = args.rememberMe + const isProduction = TRACKER_PRODUCTION === 'true' + if (isProduction === false) { + console.warn(`User: ${userName} tried to sign up but did not meet requirements.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`User is trying to register for a non-production environment.`), + } + } + // Check to make sure password meets length requirement if (password.length < 12) { console.warn(`User: ${userName} tried to sign up but did not meet requirements.`) @@ -112,16 +118,20 @@ export const signUp = new mutationWithClientMutationId({ const refreshId = uuidv4() const tfaCode = Math.floor(100000 + Math.random() * 900000) + // dynamically grabs email sub options + const emailUpdateOptions = Object.fromEntries( + Object.keys(emailUpdateOptionsType.getFields()).map((option) => [option, true]), + ) + // Create User Structure for insert const user = { displayName: displayName, userName: userName, password: hashedPassword, - preferredLang: preferredLang, phoneValidated: false, emailValidated: false, insideUser: false, - receiveUpdateEmails: true, + emailUpdateOptions, failedLoginAttempts: 0, tfaSendMethod: 'email', tfaCode: tfaCode, @@ -154,6 +164,7 @@ export const signUp = new mutationWithClientMutationId({ console.error( `Transaction step error occurred while user: ${userName} attempted to sign up, creating user: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to sign up. Please try again.`)) } @@ -162,6 +173,7 @@ export const signUp = new mutationWithClientMutationId({ insertedUser = await insertedUserCursor.next() } catch (err) { console.error(`Cursor error occurred while user: ${userName} attempted to sign up, creating user: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to sign up. Please try again.`)) } @@ -178,6 +190,7 @@ export const signUp = new mutationWithClientMutationId({ if (userName !== tokenUserName) { console.warn(`User: ${userName} attempted to sign up with an invite token, however emails do not match.`) + await trx.abort() return { _type: 'error', code: 400, @@ -188,6 +201,7 @@ export const signUp = new mutationWithClientMutationId({ const checkOrg = await loadOrgByKey.load(tokenOrgKey) if (typeof checkOrg === 'undefined') { console.warn(`User: ${userName} attempted to sign up with an invite token, however the org could not be found.`) + await trx.abort() return { _type: 'error', code: 400, @@ -211,6 +225,7 @@ export const signUp = new mutationWithClientMutationId({ console.error( `Transaction step error occurred while user: ${userName} attempted to sign up, assigning affiliation: ${err}`, ) + await trx.abort() throw new Error(i18n._(t`Unable to sign up. Please try again.`)) } } @@ -219,6 +234,7 @@ export const signUp = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Transaction commit error occurred while user: ${userName} attempted to sign up: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to sign up. Please try again.`)) } @@ -238,6 +254,7 @@ export const signUp = new mutationWithClientMutationId({ query, initiatedBy: { userName, + ipAddress: ip, }, action: 'create', target: { diff --git a/api/src/user/mutations/update-user-password.js b/api/src/user/mutations/update-user-password.js index 04f4efb84..78d07d6b6 100644 --- a/api/src/user/mutations/update-user-password.js +++ b/api/src/user/mutations/update-user-password.js @@ -91,6 +91,7 @@ export const updateUserPassword = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred when user: ${user._key} attempted to update their password: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to update password. Please try again.`)) } @@ -98,6 +99,7 @@ export const updateUserPassword = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred when user: ${user._key} attempted to update their password: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to update password. Please try again.`)) } diff --git a/api/src/user/mutations/update-user-profile.js b/api/src/user/mutations/update-user-profile.js index 9b6fb6160..b7619fc81 100644 --- a/api/src/user/mutations/update-user-profile.js +++ b/api/src/user/mutations/update-user-profile.js @@ -3,8 +3,9 @@ import { mutationWithClientMutationId } from 'graphql-relay' import { GraphQLEmailAddress } from 'graphql-scalars' import { t } from '@lingui/macro' -import { LanguageEnums, TfaSendMethodEnum } from '../../enums' +import { TfaSendMethodEnum } from '../../enums' import { updateUserProfileUnion } from '../unions' +import { emailUpdatesInput } from '../inputs/email-update-options' const { AUTHENTICATED_KEY, AUTH_TOKEN_EXPIRY } = process.env @@ -21,10 +22,6 @@ export const updateUserProfile = new mutationWithClientMutationId({ type: GraphQLEmailAddress, description: 'The updated user name the user wishes to change to.', }, - preferredLang: { - type: LanguageEnums, - description: 'The updated preferred language the user wishes to change to.', - }, tfaSendMethod: { type: TfaSendMethodEnum, description: 'The method in which the user wishes to have their TFA code sent via.', @@ -33,9 +30,10 @@ export const updateUserProfile = new mutationWithClientMutationId({ type: GraphQLBoolean, description: 'The updated boolean which represents if the user wants to see features in progress.', }, - receiveUpdateEmails: { - type: GraphQLBoolean, - description: 'The updated boolean which represents if the user wants to receive update emails.', + emailUpdateOptions: { + type: emailUpdatesInput, + description: + 'A number of different emails the user can optionally receive periodically that provide updates about their organization.', }, }), outputFields: () => ({ @@ -64,10 +62,9 @@ export const updateUserProfile = new mutationWithClientMutationId({ // Cleanse Input const displayName = cleanseInput(args.displayName) const userName = cleanseInput(args.userName).toLowerCase() - const preferredLang = cleanseInput(args.preferredLang) const subTfaSendMethod = cleanseInput(args.tfaSendMethod) const insideUserBool = args.insideUser - const receiveUpdateEmailsBool = args.receiveUpdateEmails + const emailUpdateOptions = args.emailUpdateOptions // Get user info from DB const user = await userRequired() @@ -125,23 +122,17 @@ export const updateUserProfile = new mutationWithClientMutationId({ tfaSendMethod = user.tfaSendMethod } - let emailValidated = user.emailValidated let changedUserName = false if (userName !== user.userName && userName !== '') { changedUserName = true - emailValidated = false } - // Create object containing updated data + // Create object containing updated data expect username. Username is handled separately for verification. const updatedUser = { displayName: displayName || user.displayName, - userName: userName || user.userName, - preferredLang: preferredLang || user.preferredLang, tfaSendMethod: tfaSendMethod, - emailValidated, insideUser: typeof insideUserBool !== 'undefined' ? insideUserBool : user?.insideUser, - receiveUpdateEmails: - typeof receiveUpdateEmailsBool !== 'undefined' ? receiveUpdateEmailsBool : user?.receiveUpdateEmails, + emailUpdateOptions: typeof emailUpdateOptions !== 'undefined' ? emailUpdateOptions : user?.emailUpdateOptions, } // Setup Transaction @@ -159,6 +150,7 @@ export const updateUserProfile = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred when user: ${userKey} attempted to update their profile: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to update profile. Please try again.`)) } @@ -166,6 +158,7 @@ export const updateUserProfile = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred when user: ${userKey} attempted to update their profile: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to update profile. Please try again.`)) } @@ -175,13 +168,18 @@ export const updateUserProfile = new mutationWithClientMutationId({ if (changedUserName) { const token = tokenize({ expiresIn: AUTH_TOKEN_EXPIRY, - parameters: { userKey: returnUser._key }, + parameters: { userKey: returnUser._key, userName: userName }, secret: String(AUTHENTICATED_KEY), }) const verifyUrl = `https://${request.get('host')}/validate/${token}` - await sendVerificationEmail({ user: returnUser, verifyUrl }) + await sendVerificationEmail({ + userName: userName, + displayName: returnUser.displayName, + verifyUrl: verifyUrl, + userKey: returnUser._key, + }) } console.info(`User: ${userKey} successfully updated their profile.`) diff --git a/api/src/user/mutations/verify-account.js b/api/src/user/mutations/verify-account.js index 2ea71f387..19fa3a894 100644 --- a/api/src/user/mutations/verify-account.js +++ b/api/src/user/mutations/verify-account.js @@ -6,7 +6,8 @@ import { verifyAccountUnion } from '../unions' export const verifyAccount = new mutationWithClientMutationId({ name: 'VerifyAccount', - description: 'This mutation allows the user to verify their account through a token sent in an email.', + description: + 'This mutation allows the user to switch usernames/verify their account through a token sent in an email.', inputFields: () => ({ verifyTokenString: { type: new GraphQLNonNull(GraphQLString), @@ -28,7 +29,8 @@ export const verifyAccount = new mutationWithClientMutationId({ collections, transaction, auth: { verifyToken }, - loaders: { loadUserByKey }, + loaders: { loadUserByKey, loadUserByUserName }, + notify: { sendUpdatedUserNameEmail }, validators: { cleanseInput }, }, ) => { @@ -50,9 +52,21 @@ export const verifyAccount = new mutationWithClientMutationId({ } } + // Check to see if userName exists in tokenParameters + if (!tokenParameters?.userName) { + console.warn( + `When validating account, user attempted to verify account, but userName is not located in the token parameters.`, + ) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to verify account. Please request a new email.`), + } + } + // Auth shouldn't be needed with this // Check if user exists - const { userKey } = tokenParameters + const { userKey, userName: newUserName } = tokenParameters const user = await loadUserByKey.load(userKey) if (typeof user === 'undefined') { @@ -64,6 +78,30 @@ export const verifyAccount = new mutationWithClientMutationId({ } } + // Ensure newUserName is still not already in use + const checkUser = await loadUserByUserName.load(newUserName) + if (typeof checkUser !== 'undefined') { + console.warn(`User: ${userKey} attempted to update their username, but the username is already in use.`) + return { + _type: 'error', + code: 400, + description: i18n._(t`Username not available, please try another.`), + } + } + + // Send email to current email address + try { + await sendUpdatedUserNameEmail({ + previousUserName: user.userName, + newUserName, + displayName: user.displayName, + userKey, + }) + } catch (err) { + console.error(`Error occurred when sending updated username email for ${userKey}: ${err}`) + throw new Error(i18n._(t`Unable to send updated username email. Please try again.`)) + } + // Setup Transaction const trx = await transaction(collections) @@ -73,13 +111,20 @@ export const verifyAccount = new mutationWithClientMutationId({ () => query` WITH users UPSERT { _key: ${user._key} } - INSERT { emailValidated: true } - UPDATE { emailValidated: true } + INSERT { + emailValidated: true, + userName: ${newUserName}, + } + UPDATE { + emailValidated: true, + userName: ${newUserName}, + } IN users `, ) } catch (err) { console.error(`Trx step error occurred when upserting email validation for user: ${user._key}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to verify account. Please try again.`)) } @@ -87,6 +132,7 @@ export const verifyAccount = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred when upserting email validation for user: ${user._key}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to verify account. Please try again.`)) } @@ -94,7 +140,7 @@ export const verifyAccount = new mutationWithClientMutationId({ return { _type: 'success', - status: i18n._(t`Successfully email verified account, and set TFA send method to email.`), + status: i18n._(t`Successfully email verified account.`), } }, }) diff --git a/api/src/user/mutations/verify-phone-number.js b/api/src/user/mutations/verify-phone-number.js index 619eeefaa..bcbf7d0fc 100644 --- a/api/src/user/mutations/verify-phone-number.js +++ b/api/src/user/mutations/verify-phone-number.js @@ -68,6 +68,7 @@ export const verifyPhoneNumber = new mutationWithClientMutationId({ ) } catch (err) { console.error(`Trx step error occurred when upserting the tfaValidate field for ${user._key}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to two factor authenticate. Please try again.`)) } @@ -75,6 +76,7 @@ export const verifyPhoneNumber = new mutationWithClientMutationId({ await trx.commit() } catch (err) { console.error(`Trx commit error occurred when upserting the tfaValidate field for ${user._key}: ${err}`) + await trx.abort() throw new Error(i18n._(t`Unable to two factor authenticate. Please try again.`)) } diff --git a/api/src/user/objects/__tests__/auth-result.test.js b/api/src/user/objects/__tests__/auth-result.test.js index 45199a429..7ce3e2734 100644 --- a/api/src/user/objects/__tests__/auth-result.test.js +++ b/api/src/user/objects/__tests__/auth-result.test.js @@ -1,6 +1,6 @@ -import {GraphQLString} from 'graphql' +import { GraphQLString } from 'graphql' -import {authResultType, userPersonalType} from '../index' +import { authResultType, userPersonalType } from '../index' describe('given the auth result gql object', () => { describe('testing field definitions', () => { @@ -23,9 +23,7 @@ describe('given the auth result gql object', () => { it('returns the resolved field', () => { const demoType = authResultType.getFields() - expect(demoType.authToken.resolve({token: 'authToken'})).toEqual( - 'authToken', - ) + expect(demoType.authToken.resolve({ token: 'authToken' })).toEqual('authToken') }) }) describe('testing the user field', () => { @@ -35,7 +33,6 @@ describe('given the auth result gql object', () => { const user = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, } @@ -43,12 +40,11 @@ describe('given the auth result gql object', () => { const expectedResult = { userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, } - expect(demoType.user.resolve({user})).toEqual(expectedResult) + expect(demoType.user.resolve({ user })).toEqual(expectedResult) }) }) }) diff --git a/api/src/user/objects/__tests__/complete-tour-error.test.js b/api/src/user/objects/__tests__/complete-tour-error.test.js new file mode 100644 index 000000000..686731fe0 --- /dev/null +++ b/api/src/user/objects/__tests__/complete-tour-error.test.js @@ -0,0 +1,36 @@ +import { GraphQLInt, GraphQLString } from 'graphql' +import { completeTourError } from '../complete-tour-error' + +describe('given the completeTourError object', () => { + describe('testing the field definitions', () => { + it('has an code field', () => { + const demoType = completeTourError.getFields() + + expect(demoType).toHaveProperty('code') + expect(demoType.code.type).toMatchObject(GraphQLInt) + }) + it('has a description field', () => { + const demoType = completeTourError.getFields() + + expect(demoType).toHaveProperty('description') + expect(demoType.description.type).toMatchObject(GraphQLString) + }) + }) + + describe('testing the field resolvers', () => { + describe('testing the code resolver', () => { + it('returns the resolved field', () => { + const demoType = completeTourError.getFields() + + expect(demoType.code.resolve({ code: 400 })).toEqual(400) + }) + }) + describe('testing the description field', () => { + it('returns the resolved value', () => { + const demoType = completeTourError.getFields() + + expect(demoType.description.resolve({ description: 'description' })).toEqual('description') + }) + }) + }) +}) diff --git a/api/src/user/objects/__tests__/complete-tour-result.test.js b/api/src/user/objects/__tests__/complete-tour-result.test.js new file mode 100644 index 000000000..d868fd048 --- /dev/null +++ b/api/src/user/objects/__tests__/complete-tour-result.test.js @@ -0,0 +1,49 @@ +import { GraphQLString } from 'graphql' + +import { userPersonalType } from '../user-personal' +import { completeTourResult } from '../complete-tour-result' + +describe('given the completeTourResult object', () => { + describe('testing the field definitions', () => { + it('has a status field', () => { + const demoType = completeTourResult.getFields() + + expect(demoType).toHaveProperty('status') + expect(demoType.status.type).toMatchObject(GraphQLString) + }) + it('has a user field', () => { + const demoType = completeTourResult.getFields() + + expect(demoType).toHaveProperty('user') + expect(demoType.user.type).toMatchObject(userPersonalType) + }) + }) + + describe('testing the field resolvers', () => { + describe('testing the status resolver', () => { + it('returns the resolved field', () => { + const demoType = completeTourResult.getFields() + + expect(demoType.status.resolve({ status: 'status' })).toEqual('status') + }) + }) + describe('testing the user resolver', () => { + it('returns the resolved field', () => { + const demoType = completeTourResult.getFields() + + const user = { + userName: 'user@test.com', + displayName: 'Test Account', + completedTours: [ + { + tourId: 'tour1', + completedAt: new Date(), + }, + ], + } + + expect(demoType.user.resolve({ user })).toEqual(user) + }) + }) + }) +}) diff --git a/api/src/user/objects/__tests__/dismiss-message-error.test.js b/api/src/user/objects/__tests__/dismiss-message-error.test.js new file mode 100644 index 000000000..e1a6af6d8 --- /dev/null +++ b/api/src/user/objects/__tests__/dismiss-message-error.test.js @@ -0,0 +1,37 @@ +import { GraphQLInt, GraphQLString } from 'graphql' + +import { dismissMessageError } from '../dismiss-message-error' + +describe('given the dismissMessageError object', () => { + describe('testing the field definitions', () => { + it('has an code field', () => { + const demoType = dismissMessageError.getFields() + + expect(demoType).toHaveProperty('code') + expect(demoType.code.type).toMatchObject(GraphQLInt) + }) + it('has a description field', () => { + const demoType = dismissMessageError.getFields() + + expect(demoType).toHaveProperty('description') + expect(demoType.description.type).toMatchObject(GraphQLString) + }) + }) + + describe('testing the field resolvers', () => { + describe('testing the code resolver', () => { + it('returns the resolved field', () => { + const demoType = dismissMessageError.getFields() + + expect(demoType.code.resolve({ code: 400 })).toEqual(400) + }) + }) + describe('testing the description field', () => { + it('returns the resolved value', () => { + const demoType = dismissMessageError.getFields() + + expect(demoType.description.resolve({ description: 'description' })).toEqual('description') + }) + }) + }) +}) diff --git a/api/src/user/objects/__tests__/dismiss-message-result.test.js b/api/src/user/objects/__tests__/dismiss-message-result.test.js new file mode 100644 index 000000000..870d53830 --- /dev/null +++ b/api/src/user/objects/__tests__/dismiss-message-result.test.js @@ -0,0 +1,49 @@ +import { GraphQLString } from 'graphql' + +import { dismissMessageResult } from '../dismiss-message-result' +import { userPersonalType } from '../user-personal' + +describe('given the dismissMessageResult object', () => { + describe('testing the field definitions', () => { + it('has a status field', () => { + const demoType = dismissMessageResult.getFields() + + expect(demoType).toHaveProperty('status') + expect(demoType.status.type).toMatchObject(GraphQLString) + }) + it('has a user field', () => { + const demoType = dismissMessageResult.getFields() + + expect(demoType).toHaveProperty('user') + expect(demoType.user.type).toMatchObject(userPersonalType) + }) + }) + + describe('testing the field resolvers', () => { + describe('testing the status resolver', () => { + it('returns the resolved field', () => { + const demoType = dismissMessageResult.getFields() + + expect(demoType.status.resolve({ status: 'status' })).toEqual('status') + }) + }) + describe('testing the user resolver', () => { + it('returns the resolved field', () => { + const demoType = dismissMessageResult.getFields() + + const user = { + userName: 'user@test.com', + displayName: 'Test Account', + dismissedMessage: [ + { + messageId: 'message1', + dismissedAt: new Date(), + }, + ], + } + + expect(demoType.user.resolve({ user })).toEqual(user) + }) + }) + }) +}) diff --git a/api/src/user/objects/__tests__/my-tracker-result.test.js b/api/src/user/objects/__tests__/my-tracker-result.test.js index 5a76667f8..294aed39b 100644 --- a/api/src/user/objects/__tests__/my-tracker-result.test.js +++ b/api/src/user/objects/__tests__/my-tracker-result.test.js @@ -104,10 +104,8 @@ describe('given the myTracker result gql object', () => { { _id: 'users/1' }, { first: 1 }, { - loaders: { - loadDomainConnectionsByUserId: jest - .fn() - .mockReturnValue(expectedResult), + dataSources: { + domain: { connectionsByUserId: jest.fn().mockReturnValue(expectedResult) }, }, }, ), diff --git a/api/src/user/objects/__tests__/user-personal.test.js b/api/src/user/objects/__tests__/user-personal.test.js index 31198a105..78a1d46ee 100644 --- a/api/src/user/objects/__tests__/user-personal.test.js +++ b/api/src/user/objects/__tests__/user-personal.test.js @@ -1,11 +1,11 @@ import crypto from 'crypto' -import { GraphQLNonNull, GraphQLID, GraphQLString, GraphQLBoolean } from 'graphql' +import { GraphQLNonNull, GraphQLID, GraphQLString, GraphQLBoolean, GraphQLList } from 'graphql' import { toGlobalId } from 'graphql-relay' import { GraphQLEmailAddress, GraphQLPhoneNumber } from 'graphql-scalars' import { affiliationConnection } from '../../../affiliation/objects' -import { userPersonalType } from '../index' -import { LanguageEnums, TfaSendMethodEnum } from '../../../enums' +import { completedTour, dismissedMessage, userPersonalType } from '../index' +import { TfaSendMethodEnum } from '../../../enums' import { decryptPhoneNumber } from '../../../validators' const { CIPHER_KEY } = process.env @@ -36,12 +36,6 @@ describe('given the user object', () => { expect(demoType).toHaveProperty('phoneNumber') expect(demoType.phoneNumber.type).toMatchObject(GraphQLPhoneNumber) }) - it('has a preferredLang field', () => { - const demoType = userPersonalType.getFields() - - expect(demoType).toHaveProperty('preferredLang') - expect(demoType.preferredLang.type).toMatchObject(LanguageEnums) - }) it('has a phoneValidated field', () => { const demoType = userPersonalType.getFields() @@ -66,6 +60,18 @@ describe('given the user object', () => { expect(demoType).toHaveProperty('affiliations') expect(demoType.affiliations.type).toMatchObject(affiliationConnection.connectionType) }) + it('has a dismissedMessages field', () => { + const demoType = userPersonalType.getFields() + + expect(demoType).toHaveProperty('dismissedMessages') + expect(demoType.dismissedMessages.type).toMatchObject(new GraphQLList(dismissedMessage)) + }) + it('has a completedTours field', () => { + const demoType = userPersonalType.getFields() + + expect(demoType).toHaveProperty('completedTours') + expect(demoType.completedTours.type).toMatchObject(new GraphQLList(completedTour)) + }) }) describe('testing the field resolvers', () => { describe('testing the id resolver', () => { @@ -155,13 +161,6 @@ describe('given the user object', () => { }) }) }) - describe('testing the preferredLang field', () => { - it('returns the resolved value', () => { - const demoType = userPersonalType.getFields() - - expect(demoType.preferredLang.resolve({ preferredLang: 'english' })).toEqual('english') - }) - }) describe('testing the phoneValidated field', () => { it('returns the resolved value', () => { const demoType = userPersonalType.getFields() @@ -219,13 +218,77 @@ describe('given the user object', () => { { _id: '1' }, { first: 1 }, { - loaders: { - loadAffiliationConnectionsByUserId: jest.fn().mockReturnValue(expectedResult), + dataSources: { + affiliation: { + connectionsByUserId: jest.fn().mockReturnValue(expectedResult), + }, }, }, ), ).resolves.toEqual(expectedResult) }) }) + describe('testing the dismissedMessages field', () => { + it('returns the resolved value', () => { + const demoType = userPersonalType.getFields() + + const ts = Date.now() + + expect( + demoType.dismissedMessages.resolve({ + dismissedMessages: [ + { + messageId: 'message1', + dismissedAt: ts, + }, + { + messageId: 'message2', + dismissedAt: ts, + }, + ], + }), + ).toEqual([ + { + messageId: 'message1', + dismissedAt: ts, + }, + { + messageId: 'message2', + dismissedAt: ts, + }, + ]) + }) + }) + describe('testing the completedTours field', () => { + it('returns the resolved value', () => { + const demoType = userPersonalType.getFields() + + const ts = Date.now() + + expect( + demoType.completedTours.resolve({ + completedTours: [ + { + tourId: 'tour1', + completedAt: ts, + }, + { + tourId: 'tour2', + completedAt: ts, + }, + ], + }), + ).toEqual([ + { + tourId: 'tour1', + completedAt: ts, + }, + { + tourId: 'tour2', + completedAt: ts, + }, + ]) + }) + }) }) }) diff --git a/api/src/user/objects/close-account-result.js b/api/src/user/objects/close-account-result.js index db144681c..e1bb4043d 100644 --- a/api/src/user/objects/close-account-result.js +++ b/api/src/user/objects/close-account-result.js @@ -1,14 +1,19 @@ -import {GraphQLObjectType, GraphQLString} from 'graphql' +import { GraphQLObjectType, GraphQLString } from 'graphql' +import { userSharedType } from './user-shared' export const closeAccountResult = new GraphQLObjectType({ name: 'CloseAccountResult', - description: - 'This object is used to inform the user of the status of closing their account.', + description: 'This object is used to inform the user of the status of closing their account.', fields: () => ({ status: { type: GraphQLString, description: 'Status of closing the users account.', - resolve: ({status}) => status, + resolve: ({ status }) => status, + }, + user: { + type: userSharedType, + description: 'Information of the closed user account.', + resolve: ({ user }) => user, }, }), }) diff --git a/api/src/user/objects/complete-tour-error.js b/api/src/user/objects/complete-tour-error.js new file mode 100644 index 000000000..8b71f994c --- /dev/null +++ b/api/src/user/objects/complete-tour-error.js @@ -0,0 +1,19 @@ +import { GraphQLInt, GraphQLObjectType, GraphQLString } from 'graphql' + +export const completeTourError = new GraphQLObjectType({ + name: 'CompleteTourError', + description: + 'This object is used to inform the user if confirming that they have completed the tour was unsuccessful.', + fields: () => ({ + code: { + type: GraphQLInt, + description: 'Error code to inform user what the issue is related to.', + resolve: ({ code }) => code, + }, + description: { + type: GraphQLString, + description: 'Description of the issue that was encountered.', + resolve: ({ description }) => description, + }, + }), +}) diff --git a/api/src/user/objects/complete-tour-result.js b/api/src/user/objects/complete-tour-result.js new file mode 100644 index 000000000..cf045b3b4 --- /dev/null +++ b/api/src/user/objects/complete-tour-result.js @@ -0,0 +1,20 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql' +import { userPersonalType } from './user-personal' + +export const completeTourResult = new GraphQLObjectType({ + name: 'CompleteTourResult', + description: + 'This object is used to inform the user if confirming that they have completed the tour was successful or not.', + fields: () => ({ + status: { + type: GraphQLString, + description: 'Status of the message dismissal.', + resolve: ({ status }) => status, + }, + user: { + type: userPersonalType, + description: 'The user object that was updated.', + resolve: ({ user }) => user, + }, + }), +}) diff --git a/api/src/user/objects/completed-tour.js b/api/src/user/objects/completed-tour.js new file mode 100644 index 000000000..3adc1feec --- /dev/null +++ b/api/src/user/objects/completed-tour.js @@ -0,0 +1,19 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql' +import { GraphQLDateTime } from 'graphql-scalars' + +export const completedTour = new GraphQLObjectType({ + name: 'CompletedTour', + description: 'This object is used for returning a tour that has been completed.', + fields: () => ({ + tourId: { + type: GraphQLString, + description: 'The ID of the tour that was completed.', + resolve: ({ tourId }) => tourId, + }, + completedAt: { + type: GraphQLDateTime, + description: 'The time the tour was completed.', + resolve: ({ completedAt }) => completedAt, + }, + }), +}) diff --git a/api/src/user/objects/dismiss-message-error.js b/api/src/user/objects/dismiss-message-error.js new file mode 100644 index 000000000..fc42a2554 --- /dev/null +++ b/api/src/user/objects/dismiss-message-error.js @@ -0,0 +1,18 @@ +import { GraphQLInt, GraphQLObjectType, GraphQLString } from 'graphql' + +export const dismissMessageError = new GraphQLObjectType({ + name: 'DismissMessageError', + description: 'This object is used to inform the user if any errors occurred while dismissing a message.', + fields: () => ({ + code: { + type: GraphQLInt, + description: 'Error code to inform user what the issue is related to.', + resolve: ({ code }) => code, + }, + description: { + type: GraphQLString, + description: 'Description of the issue that was encountered.', + resolve: ({ description }) => description, + }, + }), +}) diff --git a/api/src/user/objects/dismiss-message-result.js b/api/src/user/objects/dismiss-message-result.js new file mode 100644 index 000000000..a0ee09bb4 --- /dev/null +++ b/api/src/user/objects/dismiss-message-result.js @@ -0,0 +1,19 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql' +import { userPersonalType } from './user-personal' + +export const dismissMessageResult = new GraphQLObjectType({ + name: 'DismissMessageResult', + description: 'This object is used to inform the user if the message was successfully dismissed.', + fields: () => ({ + status: { + type: GraphQLString, + description: 'Status of the message dismissal.', + resolve: ({ status }) => status, + }, + user: { + type: userPersonalType, + description: 'The user object that was updated.', + resolve: ({ user }) => user, + }, + }), +}) diff --git a/api/src/user/objects/dismissed-message.js b/api/src/user/objects/dismissed-message.js new file mode 100644 index 000000000..80b7834df --- /dev/null +++ b/api/src/user/objects/dismissed-message.js @@ -0,0 +1,19 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql' +import { GraphQLDateTime } from 'graphql-scalars' + +export const dismissedMessage = new GraphQLObjectType({ + name: 'DismissedMessage', + description: 'This object is used for returning a message that has been dismissed.', + fields: () => ({ + messageId: { + type: GraphQLString, + description: 'The ID of the message that was dismissed.', + resolve: ({ messageId }) => messageId, + }, + dismissedAt: { + type: GraphQLDateTime, + description: 'The time the message was dismissed.', + resolve: ({ dismissedAt }) => dismissedAt, + }, + }), +}) diff --git a/api/src/user/objects/email-update-options.js b/api/src/user/objects/email-update-options.js new file mode 100644 index 000000000..c5467bedf --- /dev/null +++ b/api/src/user/objects/email-update-options.js @@ -0,0 +1,25 @@ +import { GraphQLBoolean, GraphQLObjectType } from 'graphql' + +export const emailUpdateOptionsType = new GraphQLObjectType({ + name: 'EmailUpdateOptions', + fields: () => ({ + orgFootprint: { + type: GraphQLBoolean, + description: + "Value used to determine if user wants to receive possibly daily email updates about their organization's digital footprint.", + resolve: ({ orgFootprint }) => orgFootprint, + }, + progressReport: { + type: GraphQLBoolean, + description: + "Value used to determine if user wants to receive monthly email updates about their organization's compliance score progress.", + resolve: ({ progressReport }) => progressReport, + }, + detectDecay: { + type: GraphQLBoolean, + description: + "Value used to determine if user wants to receive possibly daily email updates about their organization's compliance statuses.", + resolve: ({ detectDecay }) => detectDecay, + }, + }), +}) diff --git a/api/src/user/objects/index.js b/api/src/user/objects/index.js index f29ffbd81..79bb433a5 100644 --- a/api/src/user/objects/index.js +++ b/api/src/user/objects/index.js @@ -2,6 +2,13 @@ export * from './auth-result' export * from './authenticate-error' export * from './close-account-error' export * from './close-account-result' +export * from './complete-tour-error' +export * from './complete-tour-result' +export * from './completed-tour' +export * from './dismiss-message-error' +export * from './dismiss-message-result' +export * from './dismissed-message' +export * from './email-update-options' export * from './my-tracker-result' export * from './remove-phone-number-error' export * from './remove-phone-number-result' diff --git a/api/src/user/objects/my-tracker-result.js b/api/src/user/objects/my-tracker-result.js index 517ba42d8..4b10b511b 100644 --- a/api/src/user/objects/my-tracker-result.js +++ b/api/src/user/objects/my-tracker-result.js @@ -36,8 +36,8 @@ export const myTrackerType = new GraphQLObjectType({ }, ...connectionArgs, }, - resolve: async ({ _id }, args, { loaders: { loadDomainConnectionsByUserId } }) => { - const connections = await loadDomainConnectionsByUserId({ + resolve: async ({ _id }, args, { dataSources: { domain: domainDataSource } }) => { + const connections = await domainDataSource.connectionsByUserId({ ...args, myTracker: true, }) diff --git a/api/src/user/objects/user-personal.js b/api/src/user/objects/user-personal.js index cbb871c68..7580e648c 100644 --- a/api/src/user/objects/user-personal.js +++ b/api/src/user/objects/user-personal.js @@ -1,11 +1,14 @@ -import { GraphQLBoolean, GraphQLObjectType, GraphQLString } from 'graphql' +import { GraphQLBoolean, GraphQLList, GraphQLObjectType, GraphQLString } from 'graphql' import { connectionArgs, globalIdField } from 'graphql-relay' import { GraphQLEmailAddress, GraphQLPhoneNumber } from 'graphql-scalars' import { affiliationOrgOrder } from '../../affiliation/inputs' import { affiliationConnection } from '../../affiliation/objects' -import { LanguageEnums, TfaSendMethodEnum } from '../../enums' +import { TfaSendMethodEnum } from '../../enums' import { nodeInterface } from '../../node' +import { emailUpdateOptionsType } from './email-update-options' +import { dismissedMessage } from './dismissed-message' +import { completedTour } from './completed-tour' export const userPersonalType = new GraphQLObjectType({ name: 'PersonalUser', @@ -31,11 +34,6 @@ export const userPersonalType = new GraphQLObjectType({ return decryptPhoneNumber(phoneDetails) }, }, - preferredLang: { - type: LanguageEnums, - description: 'Users preferred language.', - resolve: ({ preferredLang }) => preferredLang, - }, phoneValidated: { type: GraphQLBoolean, description: 'Has the user completed phone validation.', @@ -56,10 +54,11 @@ export const userPersonalType = new GraphQLObjectType({ description: 'Does the user want to see new features in progress.', resolve: ({ insideUser }) => insideUser, }, - receiveUpdateEmails: { - type: GraphQLBoolean, - description: 'Does the user want to receive update emails.', - resolve: ({ receiveUpdateEmails }) => receiveUpdateEmails, + emailUpdateOptions: { + type: emailUpdateOptionsType, + description: + 'A number of different emails the user can optionally receive periodically that provide updates about their organization.', + resolve: ({ emailUpdateOptions }) => emailUpdateOptions, }, affiliations: { type: affiliationConnection.connectionType, @@ -75,14 +74,24 @@ export const userPersonalType = new GraphQLObjectType({ }, ...connectionArgs, }, - resolve: async ({ _id }, args, { loaders: { loadAffiliationConnectionsByUserId } }) => { - const affiliations = await loadAffiliationConnectionsByUserId({ + resolve: async ({ _id }, args, { dataSources: { affiliation } }) => { + const affiliations = await affiliation.connectionsByUserId({ userId: _id, ...args, }) return affiliations }, }, + dismissedMessages: { + type: new GraphQLList(dismissedMessage), + description: 'Messages that the user has dismissed.', + resolve: ({ dismissedMessages }) => dismissedMessages || [], + }, + completedTours: { + type: new GraphQLList(completedTour), + description: 'Tours the user has completed.', + resolve: ({ completedTours }) => completedTours || [], + }, }), interfaces: [nodeInterface], description: `This object is used for showing personal user details, diff --git a/api/src/user/objects/user-shared.js b/api/src/user/objects/user-shared.js index 188c3c0e5..bf6006cab 100644 --- a/api/src/user/objects/user-shared.js +++ b/api/src/user/objects/user-shared.js @@ -47,9 +47,9 @@ export const userSharedType = new GraphQLObjectType({ resolve: async ( {_id}, args, - {loaders: {loadAffiliationConnectionsByUserId}}, + {dataSources: {affiliation}}, ) => { - const affiliations = await loadAffiliationConnectionsByUserId({ + const affiliations = await affiliation.connectionsByUserId({ userId: _id, ...args, }) diff --git a/api/src/user/queries/__tests__/find-me.test.js b/api/src/user/queries/__tests__/find-me.test.js index 707c92a76..58c81fee6 100644 --- a/api/src/user/queries/__tests__/find-me.test.js +++ b/api/src/user/queries/__tests__/find-me.test.js @@ -1,13 +1,12 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { userRequired } from '../../../auth' import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' -import { loadAffiliationConnectionsByUserId } from '../../../affiliation/loaders' import { loadUserByKey } from '../../loaders' -import { cleanseInput } from '../../../validators' import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -48,7 +47,6 @@ describe('given the findMe query', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -82,13 +80,6 @@ describe('given the findMe query', () => { loadUserByKey: loadUserByKey({ query }), }), }, - loaders: { - loadAffiliationConnectionsByUserId: loadAffiliationConnectionsByUserId({ - query, - userKey: user._key, - cleanseInput, - }), - }, }, }) diff --git a/api/src/user/queries/__tests__/find-my-tracker.test.js b/api/src/user/queries/__tests__/find-my-tracker.test.js index 8170e2b26..27791a599 100644 --- a/api/src/user/queries/__tests__/find-my-tracker.test.js +++ b/api/src/user/queries/__tests__/find-my-tracker.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { setupI18n } from '@lingui/core' @@ -9,7 +10,7 @@ import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey, loadMyTrackerByUserId } from '../../loaders' -import { loadDomainConnectionsByUserId } from '../../../domain/loaders' +import { DomainDataSource } from '../../../domain/data-source' import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -53,7 +54,6 @@ describe('given findMyTracker query', () => { user = await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'french', emailValidated: true, }) @@ -200,10 +200,10 @@ describe('given findMyTracker query', () => { } `, rootValue: null, - contextValue: { - i18n, - userKey: user._key, - auth: { + contextValue: { + i18n, + userKey: user._key, + auth: { userRequired: userRequired({ i18n, userKey: user._key, @@ -215,23 +215,26 @@ describe('given findMyTracker query', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadMyTrackerByUserId: loadMyTrackerByUserId({ - query, - userKey: user._key, - cleanseInput, - language: 'en', - }), - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ - query, - userKey: user._key, - cleanseInput, - language: 'en', - auth: { loginRequiredBool: true }, - }), - }, + loaders: { + loadMyTrackerByUserId: loadMyTrackerByUserId({ + query, + userKey: user._key, + cleanseInput, + language: 'en', + }), }, - }) + dataSources: { + domain: new DomainDataSource({ + query, + userKey: user._key, + cleanseInput, + language: 'en', + loginRequiredBool: true, + i18n, + }), + }, + }, + }) const expectedResponse = { data: { @@ -254,11 +257,6 @@ describe('given findMyTracker query', () => { }, dmarcPhase: { categories: [ - { - name: 'not implemented', - count: 0, - percentage: 0, - }, { name: 'assess', count: 0, diff --git a/api/src/user/queries/__tests__/find-my-users.test.js b/api/src/user/queries/__tests__/find-my-users.test.js index 0d3da251d..f4a7bed84 100644 --- a/api/src/user/queries/__tests__/find-my-users.test.js +++ b/api/src/user/queries/__tests__/find-my-users.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -53,21 +54,18 @@ describe('given findMyUsersQuery', () => { superAdmin = await collections.users.save({ displayName: 'Super Admin', userName: 'super.admin@istio.actually.exists', - preferredLang: 'english', emailValidated: true, }) user1 = await collections.users.save({ displayName: 'Test Account', userName: 'test.account@istio.actually.exists', - preferredLang: 'french', emailValidated: true, }) user2 = await collections.users.save({ displayName: 'Real User', userName: 'real.user@istio.actually.exists', - preferredLang: 'english', emailValidated: true, }) diff --git a/api/src/user/queries/__tests__/find-user-by-username.test.js b/api/src/user/queries/__tests__/find-user-by-username.test.js index 32690059e..6006d1541 100644 --- a/api/src/user/queries/__tests__/find-user-by-username.test.js +++ b/api/src/user/queries/__tests__/find-user-by-username.test.js @@ -1,5 +1,6 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -55,14 +56,12 @@ describe('given the findUserByUsername query', () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) userTwo = await collections.users.save({ userName: 'test.accounttwo@istio.actually.exists', displayName: 'Test Account Two', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/user/queries/__tests__/is-user-admin.test.js b/api/src/user/queries/__tests__/is-user-admin.test.js index 536ad86a2..3328d3ec5 100644 --- a/api/src/user/queries/__tests__/is-user-admin.test.js +++ b/api/src/user/queries/__tests__/is-user-admin.test.js @@ -1,5 +1,6 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLError, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' @@ -55,7 +56,6 @@ describe('given the isUserAdmin query', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/user/queries/__tests__/is-user-super-admin.test.js b/api/src/user/queries/__tests__/is-user-super-admin.test.js index 844239b72..48dfa1cf3 100644 --- a/api/src/user/queries/__tests__/is-user-super-admin.test.js +++ b/api/src/user/queries/__tests__/is-user-super-admin.test.js @@ -1,5 +1,6 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLError, GraphQLSchema } from 'graphql' import { checkPermission, userRequired } from '../../../auth' @@ -52,7 +53,6 @@ describe('given the isUserSuperAdmin query', () => { await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) diff --git a/api/src/user/queries/find-my-users.js b/api/src/user/queries/find-my-users.js index 7272427ea..38ae19897 100644 --- a/api/src/user/queries/find-my-users.js +++ b/api/src/user/queries/find-my-users.js @@ -1,15 +1,15 @@ -import {GraphQLString} from 'graphql' -import {connectionArgs} from 'graphql-relay' +import { GraphQLString } from 'graphql' +import { connectionArgs } from 'graphql-relay' -import {affiliationUserOrder} from '../../affiliation/inputs' -import {userConnection} from '../objects/user-connection' +import { userOrder } from '../../user/inputs' +import { userConnection } from '../objects/user-connection' export const findMyUsers = { type: userConnection.connectionType, description: 'Select users an admin has access to.', args: { orderBy: { - type: affiliationUserOrder, + type: userOrder, description: 'Ordering options for user affiliation', }, search: { @@ -23,20 +23,15 @@ export const findMyUsers = { args, { userKey, - auth: { - checkSuperAdmin, - userRequired, - verifiedRequired, - superAdminRequired, - }, - loaders: {loadUserConnectionsByUserId}, + auth: { checkSuperAdmin, userRequired, verifiedRequired, superAdminRequired }, + loaders: { loadUserConnectionsByUserId }, }, ) => { const user = await userRequired() - verifiedRequired({user}) + verifiedRequired({ user }) const isSuperAdmin = await checkSuperAdmin() - superAdminRequired({user, isSuperAdmin}) + superAdminRequired({ user, isSuperAdmin }) const userConnections = await loadUserConnectionsByUserId({ isSuperAdmin, diff --git a/api/src/user/queries/is-user-admin.js b/api/src/user/queries/is-user-admin.js index 2f454bce5..108947b7a 100644 --- a/api/src/user/queries/is-user-admin.js +++ b/api/src/user/queries/is-user-admin.js @@ -24,20 +24,14 @@ export const isUserAdmin = { }, ) => { const { id: orgKey } = fromGlobalId(cleanseInput(args.orgId)) - const user = await userRequired() // check if for a specific org - if (orgKey !== '') { + if (orgKey) { const org = await loadOrgByKey.load(orgKey) - const permission = await checkPermission({ orgId: org._id }) - if (permission === 'admin' || permission === 'super_admin') { - return true - } - - return false + return ['admin', 'owner', 'super_admin'].includes(permission) } // check to see if user is an admin or higher for at least one org @@ -55,10 +49,6 @@ export const isUserAdmin = { throw new Error(i18n._(t`Unable to verify if user is an admin, please try again.`)) } - if (userAdmin.count > 0) { - return true - } - - return false + return userAdmin.count > 0 }, } diff --git a/api/src/user/unions/__tests__/complete-tour-union.test.js b/api/src/user/unions/__tests__/complete-tour-union.test.js new file mode 100644 index 000000000..7dd388004 --- /dev/null +++ b/api/src/user/unions/__tests__/complete-tour-union.test.js @@ -0,0 +1,41 @@ +import { completeTourResult, completeTourError } from '../../objects/index' +import { completeTourUnion } from '../complete-tour-union' + +describe('given the completeTourUnion', () => { + describe('testing the field types', () => { + it('contains completeTourResult', () => { + const demoType = completeTourUnion.getTypes() + + expect(demoType).toContain(completeTourResult) + }) + it('contains completeTourError', () => { + const demoType = completeTourUnion.getTypes() + + expect(demoType).toContain(completeTourError) + }) + }) + describe('testing the field selection', () => { + describe('testing the completeTourResult', () => { + it('returns the correct type', () => { + const obj = { + _type: 'success', + status: '', + user: {}, + } + + expect(completeTourUnion.resolveType(obj)).toMatch(completeTourResult.name) + }) + }) + describe('testing the completeTourError', () => { + it('returns the correct type', () => { + const obj = { + _type: 'error', + code: 400, + description: '', + } + + expect(completeTourUnion.resolveType(obj)).toMatch(completeTourError.name) + }) + }) + }) +}) diff --git a/api/src/user/unions/__tests__/dismiss-message-union.test.js b/api/src/user/unions/__tests__/dismiss-message-union.test.js new file mode 100644 index 000000000..e4c81abdb --- /dev/null +++ b/api/src/user/unions/__tests__/dismiss-message-union.test.js @@ -0,0 +1,41 @@ +import { dismissMessageResult, dismissMessageError } from '../../objects/index' +import { dismissMessageUnion } from '../dismiss-message-union' + +describe('given the dismissMessageUnion', () => { + describe('testing the field types', () => { + it('contains dismissMessageResult', () => { + const demoType = dismissMessageUnion.getTypes() + + expect(demoType).toContain(dismissMessageResult) + }) + it('contains dismissMessageError', () => { + const demoType = dismissMessageUnion.getTypes() + + expect(demoType).toContain(dismissMessageError) + }) + }) + describe('testing the field selection', () => { + describe('testing the dismissMessageResult', () => { + it('returns the correct type', () => { + const obj = { + _type: 'success', + status: '', + user: {}, + } + + expect(dismissMessageUnion.resolveType(obj)).toMatch(dismissMessageResult.name) + }) + }) + describe('testing the dismissMessageError', () => { + it('returns the correct type', () => { + const obj = { + _type: 'error', + code: 400, + description: '', + } + + expect(dismissMessageUnion.resolveType(obj)).toMatch(dismissMessageError.name) + }) + }) + }) +}) diff --git a/api/src/user/unions/complete-tour-union.js b/api/src/user/unions/complete-tour-union.js new file mode 100644 index 000000000..24b140088 --- /dev/null +++ b/api/src/user/unions/complete-tour-union.js @@ -0,0 +1,16 @@ +import { GraphQLUnionType } from 'graphql' +import { completeTourError, completeTourResult } from '../objects' + +export const completeTourUnion = new GraphQLUnionType({ + name: 'CompleteTourUnion', + description: + 'This union is used to inform the user if confirming that they have completed the tour was successful or not.', + types: [completeTourResult, completeTourError], + resolveType({ _type }) { + if (_type === 'success') { + return completeTourResult.name + } else { + return completeTourError.name + } + }, +}) diff --git a/api/src/user/unions/dismiss-message-union.js b/api/src/user/unions/dismiss-message-union.js new file mode 100644 index 000000000..6f7be0531 --- /dev/null +++ b/api/src/user/unions/dismiss-message-union.js @@ -0,0 +1,16 @@ +import { GraphQLUnionType } from 'graphql' +import { dismissMessageError, dismissMessageResult } from '../objects' + +export const dismissMessageUnion = new GraphQLUnionType({ + name: 'DismissMessageUnion', + description: + 'This union is used to inform the user if the message was successfully dismissed or if any errors occurred while dismissing a message.', + types: [dismissMessageError, dismissMessageResult], + resolveType({ _type }) { + if (_type === 'success') { + return dismissMessageResult.name + } else { + return dismissMessageError.name + } + }, +}) diff --git a/api/src/user/unions/index.js b/api/src/user/unions/index.js index 1a94618ed..a116a965b 100644 --- a/api/src/user/unions/index.js +++ b/api/src/user/unions/index.js @@ -1,5 +1,7 @@ export * from './authenticate-union' export * from './close-account-union' +export * from './complete-tour-union' +export * from './dismiss-message-union' export * from './refresh-tokens-union' export * from './remove-phone-number-union' export * from './reset-password-union' diff --git a/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-domain.test.js b/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-domain.test.js index a1afd825d..55ea1176c 100644 --- a/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-domain.test.js +++ b/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-domain.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -22,16 +23,16 @@ describe('given a loadVerifiedDomainsById dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { org = await collections.organizations.save({ @@ -135,17 +136,13 @@ describe('given a loadVerifiedDomainsById dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedDomainsById({ query: mockedQuery, i18n }) try { await loader.load('domain.ca') } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -166,9 +163,7 @@ describe('given a loadVerifiedDomainsById dataloader', () => { try { await loader.load('domain.ca') } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -194,19 +189,13 @@ describe('given a loadVerifiedDomainsById dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedDomainsById({ query: mockedQuery, i18n }) try { await loader.load('domain.ca') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -227,11 +216,7 @@ describe('given a loadVerifiedDomainsById dataloader', () => { try { await loader.load('domain.ca') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-key.test.js b/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-key.test.js index f5f485a18..0fc8226a6 100644 --- a/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-key.test.js +++ b/api/src/verified-domains/loaders/__tests__/load-verified-domain-by-key.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -22,16 +23,16 @@ describe('given a loadVerifiedDomainByKey dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { org = await collections.organizations.save({ @@ -135,17 +136,13 @@ describe('given a loadVerifiedDomainByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedDomainByKey({ query: mockedQuery, i18n }) try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -166,9 +163,7 @@ describe('given a loadVerifiedDomainByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -194,19 +189,13 @@ describe('given a loadVerifiedDomainByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedDomainByKey({ query: mockedQuery, i18n }) try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -227,11 +216,7 @@ describe('given a loadVerifiedDomainByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/verified-domains/loaders/__tests__/load-verified-domain-conn-by-org-id.test.js b/api/src/verified-domains/loaders/__tests__/load-verified-domain-conn-by-org-id.test.js index fdb966208..c956952d3 100644 --- a/api/src/verified-domains/loaders/__tests__/load-verified-domain-conn-by-org-id.test.js +++ b/api/src/verified-domains/loaders/__tests__/load-verified-domain-conn-by-org-id.test.js @@ -1,15 +1,13 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { stringify } from 'jest-matcher-utils' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' -import { - loadVerifiedDomainConnectionsByOrgId, - loadVerifiedDomainByKey, -} from '../../loaders' +import { loadVerifiedDomainConnectionsByOrgId, loadVerifiedDomainByKey } from '../../loaders' import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -30,22 +28,21 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -108,10 +105,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -154,10 +148,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -200,10 +191,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -245,10 +233,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1110,11 +1095,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `VerifiedDomain` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `VerifiedDomain` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -1139,11 +1120,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `VerifiedDomain` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `VerifiedDomain` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -1215,9 +1192,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnectionsByOrgId({ query, cleanseInput, @@ -1234,11 +1209,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User attempted to have \`first\` set as a ${typeof invalidInput} for: loadVerifiedDomainConnectionsByOrgId.`, @@ -1248,9 +1219,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnectionsByOrgId({ query, cleanseInput, @@ -1267,11 +1236,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User attempted to have \`last\` set as a ${typeof invalidInput} for: loadVerifiedDomainConnectionsByOrgId.`, @@ -1284,9 +1249,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { describe('given a database error', () => { describe('when gathering domain keys that are claimed by orgs that the user has affiliations to', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database Error Occurred.')) const connectionLoader = loadVerifiedDomainConnectionsByOrgId({ query, @@ -1303,9 +1266,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -1339,9 +1300,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -1442,11 +1401,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`first` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ @@ -1471,11 +1426,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`last` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ @@ -1547,9 +1498,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnectionsByOrgId({ query, cleanseInput, @@ -1567,9 +1516,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -1580,9 +1527,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnectionsByOrgId({ query, cleanseInput, @@ -1600,9 +1545,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -1616,9 +1559,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { describe('given a database error', () => { describe('when gathering domain keys that are claimed by orgs that the user has affiliations to', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database Error Occurred.')) const connectionLoader = loadVerifiedDomainConnectionsByOrgId({ query, @@ -1635,11 +1576,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -1673,11 +1610,7 @@ describe('given the loadVerifiedDomainConnectionsByOrgId function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/verified-domains/loaders/__tests__/load-verified-domain-connections.test.js b/api/src/verified-domains/loaders/__tests__/load-verified-domain-connections.test.js index cba8bbeec..c5d31a548 100644 --- a/api/src/verified-domains/loaders/__tests__/load-verified-domain-connections.test.js +++ b/api/src/verified-domains/loaders/__tests__/load-verified-domain-connections.test.js @@ -1,15 +1,13 @@ import { setupI18n } from '@lingui/core' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { stringify } from 'jest-matcher-utils' import { toGlobalId } from 'graphql-relay' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' -import { - loadVerifiedDomainConnections, - loadVerifiedDomainByKey, -} from '../../loaders' +import { loadVerifiedDomainConnections, loadVerifiedDomainByKey } from '../../loaders' import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -30,22 +28,21 @@ describe('given the load domain connection using org id function', () => { describe('given a successful load', () => { beforeAll(async () => { ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { user = await collections.users.save({ userName: 'test.account@istio.actually.exists', displayName: 'Test Account', - preferredLang: 'french', tfaValidated: false, emailValidated: false, }) @@ -108,10 +105,7 @@ describe('given the load domain connection using org id function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -153,10 +147,7 @@ describe('given the load domain connection using org id function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -198,10 +189,7 @@ describe('given the load domain connection using org id function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -242,10 +230,7 @@ describe('given the load domain connection using org id function', () => { }) const domainLoader = loadVerifiedDomainByKey({ query }) - const expectedDomains = await domainLoader.loadMany([ - domain._key, - domainTwo._key, - ]) + const expectedDomains = await domainLoader.loadMany([domain._key, domainTwo._key]) expectedDomains[0].id = expectedDomains[0]._key expectedDomains[1].id = expectedDomains[1]._key @@ -1088,11 +1073,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `VerifiedDomain` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `VerifiedDomain` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -1116,11 +1097,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `VerifiedDomain` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `VerifiedDomain` connection cannot be less than zero.')) } expect(consoleOutput).toEqual([ @@ -1190,9 +1167,7 @@ describe('given the load domain connection using org id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnections({ query, cleanseInput, @@ -1208,11 +1183,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User attempted to have \`first\` set as a ${typeof invalidInput} for: loadVerifiedDomainConnections.`, @@ -1222,9 +1193,7 @@ describe('given the load domain connection using org id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnections({ query, cleanseInput, @@ -1240,11 +1209,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User attempted to have \`last\` set as a ${typeof invalidInput} for: loadVerifiedDomainConnections.`, @@ -1257,9 +1222,7 @@ describe('given the load domain connection using org id function', () => { describe('given a database error', () => { describe('when gathering domain keys that are claimed by orgs that the user has affiliations to', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database Error Occurred.')) const connectionLoader = loadVerifiedDomainConnections({ query, @@ -1275,9 +1238,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -1311,9 +1272,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error('Unable to load verified domain(s). Please try again.'), - ) + expect(err).toEqual(new Error('Unable to load verified domain(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -1411,11 +1370,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`first` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ @@ -1439,11 +1394,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.', - ), - ) + expect(err).toEqual(new Error('`last` sur la connexion `VerifiedDomain` ne peut être inférieur à zéro.')) } expect(consoleOutput).toEqual([ @@ -1513,9 +1464,7 @@ describe('given the load domain connection using org id function', () => { describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnections({ query, cleanseInput, @@ -1532,9 +1481,7 @@ describe('given the load domain connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -1545,9 +1492,7 @@ describe('given the load domain connection using org id function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedDomainConnections({ query, cleanseInput, @@ -1564,9 +1509,7 @@ describe('given the load domain connection using org id function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -1580,9 +1523,7 @@ describe('given the load domain connection using org id function', () => { describe('given a database error', () => { describe('when gathering domain keys that are claimed by orgs that the user has affiliations to', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database Error Occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database Error Occurred.')) const connectionLoader = loadVerifiedDomainConnections({ query, @@ -1598,11 +1539,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -1635,11 +1572,7 @@ describe('given the load domain connection using org id function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) domaine(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ diff --git a/api/src/verified-domains/queries/__tests__/find-verified-domain-by-domain.test.js b/api/src/verified-domains/queries/__tests__/find-verified-domain-by-domain.test.js index 98dfab8a4..c9a4463a0 100644 --- a/api/src/verified-domains/queries/__tests__/find-verified-domain-by-domain.test.js +++ b/api/src/verified-domains/queries/__tests__/find-verified-domain-by-domain.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' diff --git a/api/src/verified-domains/queries/__tests__/find-verified-domains.test.js b/api/src/verified-domains/queries/__tests__/find-verified-domains.test.js index 16d66525d..6857181e3 100644 --- a/api/src/verified-domains/queries/__tests__/find-verified-domains.test.js +++ b/api/src/verified-domains/queries/__tests__/find-verified-domains.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' diff --git a/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn-by-domain-id.test.js b/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn-by-domain-id.test.js index a1321e4bb..2419fbb12 100644 --- a/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn-by-domain-id.test.js +++ b/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn-by-domain-id.test.js @@ -1,15 +1,13 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' -import { - loadVerifiedOrgConnectionsByDomainId, - loadVerifiedOrgByKey, -} from '../../loaders' +import { loadVerifiedOrgConnectionsByDomainId, loadVerifiedOrgByKey } from '../../loaders' import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env @@ -27,20 +25,21 @@ const i18n = setupI18n({ }, }) +const orgOneSummaryData = { + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, +} + const orgOneData = { verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, orgDetails: { en: { slug: 'slug-org-one', @@ -65,20 +64,21 @@ const orgOneData = { }, } +const orgTwoSummaryData = { + web: { + pass: 52, + fail: 1002, + total: 1054, + }, + mail: { + pass: 52, + fail: 1002, + total: 1054, + }, +} + const orgTwoData = { verified: true, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, orgDetails: { en: { slug: 'slug-org-two', @@ -103,20 +103,21 @@ const orgTwoData = { }, } +const orgThreeSummaryData = { + web: { + pass: 51, + fail: 1001, + total: 1052, + }, + mail: { + pass: 51, + fail: 1001, + total: 1052, + }, +} + const orgThreeData = { verified: true, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, orgDetails: { en: { slug: 'slug-org-three', @@ -163,32 +164,34 @@ describe('given the load organizations connection function', () => { console.error = mockedError console.warn = mockedWarn - let query, - drop, - truncate, - collections, - org, - orgTwo, - domain, - domainTwo, - domainThree + let query, drop, truncate, collections, db, org, orgTwo, domain, domainTwo, domainThree beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) beforeEach(async () => { org = await collections.organizations.save(orgOneData) + const orgSummary = await collections.organizationSummaries.save({ + organization: org._id, + ...orgOneSummaryData, + }) + await db.collection("organizations").update(org._key, { latestSummaryId: orgSummary._id }) orgTwo = await collections.organizations.save(orgTwoData) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + ...orgTwoSummaryData, + }) + await db.collection("organizations").update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) domain = await collections.domains.save(domainOneData) domainTwo = await collections.domains.save(domainTwoData) @@ -249,10 +252,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), node: { ...expectedOrgs[1], }, @@ -262,14 +262,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), }, } @@ -283,30 +277,32 @@ describe('given the load organizations connection function', () => { console.error = mockedError console.warn = mockedWarn - let query, - drop, - truncate, - collections, - org, - orgTwo, - domain, - domainTwo, - domainThree + let query, drop, truncate, collections, db, org, orgTwo, domain, domainTwo, domainThree beforeEach(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) org = await collections.organizations.save(orgOneData) + const orgSummary = await collections.organizationSummaries.save({ + organization: org._id, + ...orgOneSummaryData, + }) + await db.collection("organizations").update(org._key, { latestSummaryId: orgSummary._id }) orgTwo = await collections.organizations.save(orgTwoData) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + ...orgTwoSummaryData, + }) + await db.collection("organizations").update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) domain = await collections.domains.save(domainOneData) domainTwo = await collections.domains.save(domainTwoData) @@ -367,10 +363,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), node: { ...expectedOrgs[0], }, @@ -380,14 +373,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), }, } @@ -401,30 +388,32 @@ describe('given the load organizations connection function', () => { console.error = mockedError console.warn = mockedWarn - let query, - drop, - truncate, - collections, - org, - orgTwo, - domain, - domainTwo, - domainThree + let query, drop, truncate, collections, db, org, orgTwo, domain, domainTwo, domainThree beforeEach(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) org = await collections.organizations.save(orgOneData) + const orgSummary = await collections.organizationSummaries.save({ + organization: org._id, + ...orgOneSummaryData, + }) + await db.collection("organizations").update(org._key, { latestSummaryId: orgSummary._id }) orgTwo = await collections.organizations.save(orgTwoData) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + ...orgTwoSummaryData, + }) + await db.collection("organizations").update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) domain = await collections.domains.save(domainOneData) domainTwo = await collections.domains.save(domainTwoData) @@ -484,10 +473,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), node: { ...expectedOrgs[0], }, @@ -497,14 +483,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), }, } @@ -518,30 +498,32 @@ describe('given the load organizations connection function', () => { console.error = mockedError console.warn = mockedWarn - let query, - drop, - truncate, - collections, - org, - orgTwo, - domain, - domainTwo, - domainThree + let query, drop, truncate, collections, db, org, orgTwo, domain, domainTwo, domainThree beforeEach(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) org = await collections.organizations.save(orgOneData) + const orgSummary = await collections.organizationSummaries.save({ + organization: org._id, + ...orgOneSummaryData, + }) + await db.collection("organizations").update(org._key, { latestSummaryId: orgSummary._id }) orgTwo = await collections.organizations.save(orgTwoData) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + ...orgTwoSummaryData, + }) + await db.collection("organizations").update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) domain = await collections.domains.save(domainOneData) domainTwo = await collections.domains.save(domainTwoData) @@ -601,10 +583,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), node: { ...expectedOrgs[1], }, @@ -614,14 +593,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), }, } @@ -635,33 +608,39 @@ describe('given the load organizations connection function', () => { console.error = mockedError console.warn = mockedWarn - let query, - drop, - truncate, - collections, - org, - orgTwo, - domain, - domainTwo, - domainThree, - orgThree + let query, drop, truncate, collections, db, org, orgTwo, domain, domainTwo, domainThree, orgThree beforeEach(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) org = await collections.organizations.save(orgOneData) + const orgSummary = await collections.organizationSummaries.save({ + organization: org._id, + ...orgOneSummaryData, + }) + await db.collection("organizations").update(org._key, { latestSummaryId: orgSummary._id }) orgTwo = await collections.organizations.save(orgTwoData) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + ...orgTwoSummaryData, + }) + await db.collection("organizations").update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) orgThree = await collections.organizations.save(orgThreeData) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + ...orgThreeSummaryData, + }) + await db.collection("organizations").update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) domain = await collections.domains.save(domainOneData) domainTwo = await collections.domains.save(domainTwoData) @@ -738,10 +717,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -751,14 +727,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -798,10 +768,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -811,14 +778,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -860,10 +821,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -873,14 +831,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -920,10 +872,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -933,14 +882,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -982,10 +925,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -995,14 +935,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1042,10 +976,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1055,14 +986,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1104,10 +1029,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1117,14 +1039,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1165,10 +1081,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1178,14 +1091,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1227,10 +1134,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1240,14 +1144,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1287,10 +1185,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1300,14 +1195,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1349,10 +1238,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1362,14 +1248,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1409,10 +1289,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1422,14 +1299,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1471,10 +1342,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1484,14 +1352,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1531,10 +1393,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1544,14 +1403,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1593,10 +1446,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1606,14 +1456,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1653,10 +1497,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1666,14 +1507,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1715,10 +1550,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1728,14 +1560,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1775,10 +1601,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1788,14 +1611,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1837,10 +1654,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1850,14 +1664,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1897,10 +1705,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1910,14 +1715,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1960,10 +1759,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1973,14 +1769,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2020,10 +1810,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2033,14 +1820,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2082,10 +1863,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2095,14 +1873,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2143,10 +1915,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2156,14 +1925,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2177,16 +1940,16 @@ describe('given the load organizations connection function', () => { beforeEach(async () => { ;({ query, drop } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) afterAll(async () => { @@ -2270,9 +2033,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User did not have either `first` or `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User did not have either `first` or `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2310,9 +2071,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `first` and `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `first` and `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2341,17 +2100,11 @@ describe('given the load organizations connection function', () => { first: -1, }) } catch (err) { - expect(err).toEqual( - new Error( - '`first` on the `VerifiedOrganization` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`first` on the `VerifiedOrganization` connection cannot be less than zero.')) } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `first` set below zero for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `first` set below zero for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2379,17 +2132,11 @@ describe('given the load organizations connection function', () => { last: -1, }) } catch (err) { - expect(err).toEqual( - new Error( - '`last` on the `VerifiedOrganization` connection cannot be less than zero.', - ), - ) + expect(err).toEqual(new Error('`last` on the `VerifiedOrganization` connection cannot be less than zero.')) } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `last` set below zero for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `last` set below zero for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2426,9 +2173,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `first` to 101 for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `first` to 101 for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2465,9 +2210,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `last` to 101 for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `last` to 101 for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2493,19 +2236,13 @@ describe('given the load organizations connection function', () => { afterAll(() => { console.warn = warn }) - it(`returns an error when first set is to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set is to ${stringify(invalidInput)}`, async () => { try { await connectionLoader({ first: invalidInput, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(console.warn.mock.calls).toEqual([ [ @@ -2536,19 +2273,13 @@ describe('given the load organizations connection function', () => { afterAll(() => { console.warn = warn }) - it(`returns an error when last is set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last is set to ${stringify(invalidInput)}`, async () => { try { await connectionLoader({ last: invalidInput, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(console.warn.mock.calls).toEqual([ [ @@ -2566,9 +2297,7 @@ describe('given the load organizations connection function', () => { console.error = jest.fn() const connectionLoader = loadVerifiedOrgConnectionsByDomainId({ - query: jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')), + query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), language: 'en', cleanseInput, i18n, @@ -2579,9 +2308,7 @@ describe('given the load organizations connection function', () => { domainId: '1234', first: 5, }), - ).rejects.toThrow( - 'Unable to load verified organization(s). Please try again.', - ) + ).rejects.toThrow('Unable to load verified organization(s). Please try again.') expect(console.error.mock.calls).toEqual([ [ @@ -2614,9 +2341,7 @@ describe('given the load organizations connection function', () => { domainId: '1234', first: 5, }), - ).rejects.toThrow( - 'Unable to load verified organization(s). Please try again.', - ) + ).rejects.toThrow('Unable to load verified organization(s). Please try again.') expect(console.error.mock.calls).toEqual([ [ @@ -2674,9 +2399,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User did not have either `first` or `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User did not have either `first` or `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2714,9 +2437,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `first` and `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `first` and `last` arguments set for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2746,16 +2467,12 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.', - ), + new Error('`first` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.'), ) } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `first` set below zero for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `first` set below zero for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2784,16 +2501,12 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`last` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.', - ), + new Error('`last` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.'), ) } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `last` set below zero for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `last` set below zero for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2830,9 +2543,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `first` to 101 for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `first` to 101 for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2869,9 +2580,7 @@ describe('given the load organizations connection function', () => { } expect(console.warn.mock.calls).toEqual([ - [ - 'User attempted to have `last` to 101 for: loadVerifiedOrgConnectionsByDomainId.', - ], + ['User attempted to have `last` to 101 for: loadVerifiedOrgConnectionsByDomainId.'], ]) }) }) @@ -2897,18 +2606,14 @@ describe('given the load organizations connection function', () => { afterAll(() => { console.warn = warn }) - it(`returns an error when first set is to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set is to ${stringify(invalidInput)}`, async () => { try { await connectionLoader({ first: invalidInput, }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(console.warn.mock.calls).toEqual([ @@ -2940,18 +2645,14 @@ describe('given the load organizations connection function', () => { afterAll(() => { console.warn = warn }) - it(`returns an error when last is set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last is set to ${stringify(invalidInput)}`, async () => { try { await connectionLoader({ last: invalidInput, }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(console.warn.mock.calls).toEqual([ @@ -2970,9 +2671,7 @@ describe('given the load organizations connection function', () => { console.error = jest.fn() const connectionLoader = loadVerifiedOrgConnectionsByDomainId({ - query: jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')), + query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), language: 'en', cleanseInput, i18n, @@ -2983,9 +2682,7 @@ describe('given the load organizations connection function', () => { domainId: '1234', first: 5, }), - ).rejects.toThrow( - 'Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.', - ) + ).rejects.toThrow('Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.') expect(console.error.mock.calls).toEqual([ [ @@ -3018,9 +2715,7 @@ describe('given the load organizations connection function', () => { domainId: '1234', first: 5, }), - ).rejects.toThrow( - 'Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.', - ) + ).rejects.toThrow('Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.') expect(console.error.mock.calls).toEqual([ [ diff --git a/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn.test.js b/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn.test.js index fb795727b..67ebce677 100644 --- a/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn.test.js +++ b/api/src/verified-organizations/loaders/__tests__/load-verified-org-conn.test.js @@ -1,5 +1,6 @@ import { stringify } from 'jest-matcher-utils' -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' @@ -12,16 +13,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given the load organizations connection function', () => { - let query, - drop, - truncate, - collections, - org, - orgTwo, - i18n, - domain, - domainTwo, - domainThree + let query, drop, truncate, collections, db, org, orgTwo, i18n, domain, domainTwo, domainThree const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -36,33 +28,21 @@ describe('given the load organizations connection function', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, + + schema: dbschema, + })) }) beforeEach(async () => { org = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, - }, orgDetails: { en: { slug: 'slug-org-one', @@ -86,20 +66,15 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgSummary = await collections.organizationSummaries.save({ + organization: org._id, + web: { pass: 50, fail: 1000, total: 1050 }, + mail: { pass: 50, fail: 1000, total: 1050 }, + }) + await db.collection('organizations').update(org._key, { latestSummaryId: orgSummary._id }) + orgTwo = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 52, - fail: 1002, - total: 1054, - }, - mail: { - pass: 52, - fail: 1002, - total: 1054, - }, - }, orgDetails: { en: { slug: 'slug-org-two', @@ -123,6 +98,12 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgTwoSummary = await collections.organizationSummaries.save({ + organization: orgTwo._id, + web: { pass: 52, fail: 1002, total: 1054 }, + mail: { pass: 52, fail: 1002, total: 1054 }, + }) + await db.collection('organizations').update(orgTwo._key, { latestSummaryId: orgTwoSummary._id }) domain = await collections.domains.save({ domain: 'test.domain.gc.ca', }) @@ -196,10 +177,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), node: { ...expectedOrgs[1], }, @@ -209,14 +187,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), }, } @@ -249,10 +221,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), node: { ...expectedOrgs[0], }, @@ -262,14 +231,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), }, } @@ -301,10 +264,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), node: { ...expectedOrgs[0], }, @@ -314,14 +274,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), }, } @@ -353,10 +307,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), node: { ...expectedOrgs[1], }, @@ -366,14 +317,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), }, } @@ -385,18 +330,6 @@ describe('given the load organizations connection function', () => { beforeEach(async () => { orgThree = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, orgDetails: { en: { slug: 'slug-org-three', @@ -420,6 +353,12 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + web: { pass: 51, fail: 1001, total: 1052 }, + mail: { pass: 51, fail: 1001, total: 1052 }, + }) + await db.collection('organizations').update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) await collections.claims.save({ _from: orgThree._id, _to: domain._id, @@ -462,10 +401,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -475,14 +411,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -521,10 +451,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -534,14 +461,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -582,10 +503,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -595,14 +513,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -641,10 +553,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -654,14 +563,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -702,10 +605,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -715,14 +615,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -761,10 +655,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -774,14 +665,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -822,10 +707,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -835,14 +717,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -881,10 +757,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -894,14 +767,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -942,10 +809,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -955,14 +819,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1001,10 +859,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1014,14 +869,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1062,10 +911,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1075,14 +921,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1121,10 +961,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1134,14 +971,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1182,10 +1013,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1195,14 +1023,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1241,10 +1063,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1254,14 +1073,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1302,10 +1115,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1315,14 +1125,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1361,10 +1165,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1374,14 +1175,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1422,10 +1217,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1435,14 +1227,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1481,10 +1267,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1494,14 +1277,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1542,10 +1319,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1555,14 +1329,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1601,10 +1369,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1614,14 +1379,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1662,10 +1421,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1675,14 +1431,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1721,10 +1471,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1734,14 +1481,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1782,10 +1523,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1795,14 +1533,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1841,10 +1573,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -1854,14 +1583,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -1944,10 +1667,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), node: { ...expectedOrgs[1], }, @@ -1957,14 +1677,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), }, } @@ -1997,10 +1711,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), node: { ...expectedOrgs[0], }, @@ -2010,14 +1721,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), }, } @@ -2049,10 +1754,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), node: { ...expectedOrgs[0], }, @@ -2062,14 +1764,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: false, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[0]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[0]._key), }, } @@ -2101,10 +1797,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), node: { ...expectedOrgs[1], }, @@ -2114,14 +1807,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: false, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrgs[1]._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrgs[1]._key), }, } @@ -2133,18 +1820,6 @@ describe('given the load organizations connection function', () => { beforeEach(async () => { orgThree = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 51, - fail: 1001, - total: 1052, - }, - mail: { - pass: 51, - fail: 1001, - total: 1052, - }, - }, orgDetails: { en: { slug: 'slug-org-three', @@ -2168,6 +1843,12 @@ describe('given the load organizations connection function', () => { }, }, }) + const orgThreeSummary = await collections.organizationSummaries.save({ + organization: orgThree._id, + web: { pass: 51, fail: 1001, total: 1052 }, + mail: { pass: 51, fail: 1001, total: 1052 }, + }) + await db.collection('organizations').update(orgThree._key, { latestSummaryId: orgThreeSummary._id }) await collections.claims.save({ _from: orgThree._id, _to: domain._id, @@ -2210,10 +1891,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2223,14 +1901,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2269,10 +1941,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2282,14 +1951,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2330,10 +1993,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2343,14 +2003,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2389,10 +2043,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2402,14 +2053,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2450,10 +2095,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2463,14 +2105,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2509,10 +2145,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2522,14 +2155,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2570,10 +2197,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2583,14 +2207,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2629,10 +2247,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2642,14 +2257,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2690,10 +2299,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2703,14 +2309,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2749,10 +2349,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2762,14 +2359,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2810,10 +2401,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2823,14 +2411,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2869,10 +2451,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2882,14 +2461,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2930,10 +2503,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -2943,14 +2513,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -2989,10 +2553,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3002,14 +2563,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3050,10 +2605,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3063,14 +2615,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3109,10 +2655,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3122,14 +2665,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3170,10 +2707,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3183,14 +2717,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3229,10 +2757,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3242,14 +2767,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3290,10 +2809,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3303,14 +2819,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3349,10 +2859,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3362,14 +2869,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3410,10 +2911,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3423,14 +2921,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3469,10 +2961,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3482,14 +2971,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3530,10 +3013,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3543,14 +3023,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3589,10 +3063,7 @@ describe('given the load organizations connection function', () => { const expectedStructure = { edges: [ { - cursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + cursor: toGlobalId('verifiedOrganization', expectedOrg._key), node: { ...expectedOrg, }, @@ -3602,14 +3073,8 @@ describe('given the load organizations connection function', () => { pageInfo: { hasNextPage: true, hasPreviousPage: true, - startCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), - endCursor: toGlobalId( - 'verifiedOrganization', - expectedOrg._key, - ), + startCursor: toGlobalId('verifiedOrganization', expectedOrg._key), + endCursor: toGlobalId('verifiedOrganization', expectedOrg._key), }, } @@ -3740,9 +3205,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` on the `VerifiedOrganization` connection cannot be less than zero.', - ), + new Error('`first` on the `VerifiedOrganization` connection cannot be less than zero.'), ) } @@ -3769,9 +3232,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`last` on the `VerifiedOrganization` connection cannot be less than zero.', - ), + new Error('`last` on the `VerifiedOrganization` connection cannot be less than zero.'), ) } @@ -3806,9 +3267,7 @@ describe('given the load organizations connection function', () => { ) } - expect(consoleOutput).toEqual([ - 'User attempted to have `first` to 101 for: loadVerifiedOrgConnections.', - ]) + expect(consoleOutput).toEqual(['User attempted to have `first` to 101 for: loadVerifiedOrgConnections.']) }) }) describe('last is set', () => { @@ -3835,18 +3294,14 @@ describe('given the load organizations connection function', () => { ) } - expect(consoleOutput).toEqual([ - 'User attempted to have `last` to 101 for: loadVerifiedOrgConnections.', - ]) + expect(consoleOutput).toEqual(['User attempted to have `last` to 101 for: loadVerifiedOrgConnections.']) }) }) }) describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedOrgConnections({ query, language: 'en', @@ -3863,11 +3318,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`first\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User attempted to have \`first\` set as a ${typeof invalidInput} for: loadVerifiedOrgConnections.`, @@ -3877,9 +3328,7 @@ describe('given the load organizations connection function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedOrgConnections({ query, language: 'en', @@ -3896,11 +3345,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - `\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`, - ), - ) + expect(err).toEqual(new Error(`\`last\` must be of type \`number\` not \`${typeof invalidInput}\`.`)) } expect(consoleOutput).toEqual([ `User attempted to have \`last\` set as a ${typeof invalidInput} for: loadVerifiedOrgConnections.`, @@ -3913,9 +3358,7 @@ describe('given the load organizations connection function', () => { describe('given a database error', () => { describe('when gathering organizations', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadVerifiedOrgConnections({ query, @@ -3930,11 +3373,7 @@ describe('given the load organizations connection function', () => { } await connectionLoader({ ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to load verified organization(s). Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to load verified organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -3967,11 +3406,7 @@ describe('given the load organizations connection function', () => { ...connectionArgs, }) } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to load verified organization(s). Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to load verified organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -4070,9 +3505,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`first` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.', - ), + new Error('`first` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.'), ) } @@ -4099,9 +3532,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - '`last` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.', - ), + new Error('`last` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro.'), ) } @@ -4136,9 +3567,7 @@ describe('given the load organizations connection function', () => { ) } - expect(consoleOutput).toEqual([ - 'User attempted to have `first` to 101 for: loadVerifiedOrgConnections.', - ]) + expect(consoleOutput).toEqual(['User attempted to have `first` to 101 for: loadVerifiedOrgConnections.']) }) }) describe('last is set', () => { @@ -4165,18 +3594,14 @@ describe('given the load organizations connection function', () => { ) } - expect(consoleOutput).toEqual([ - 'User attempted to have `last` to 101 for: loadVerifiedOrgConnections.', - ]) + expect(consoleOutput).toEqual(['User attempted to have `last` to 101 for: loadVerifiedOrgConnections.']) }) }) }) describe('limits are not set to numbers', () => { describe('first limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when first set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when first set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedOrgConnections({ query, language: 'fr', @@ -4194,9 +3619,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`first\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -4207,9 +3630,7 @@ describe('given the load organizations connection function', () => { }) describe('last limit is set', () => { ;['123', {}, [], null, true].forEach((invalidInput) => { - it(`returns an error when last set to ${stringify( - invalidInput, - )}`, async () => { + it(`returns an error when last set to ${stringify(invalidInput)}`, async () => { const connectionLoader = loadVerifiedOrgConnections({ query, language: 'fr', @@ -4227,9 +3648,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - `\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`, - ), + new Error(`\`last\` doit être de type \`number\` et non \`${typeof invalidInput}\`.`), ) } expect(consoleOutput).toEqual([ @@ -4243,9 +3662,7 @@ describe('given the load organizations connection function', () => { describe('given a database error', () => { describe('when gathering organizations', () => { it('returns an error message', async () => { - const query = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const query = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const connectionLoader = loadVerifiedOrgConnections({ query, @@ -4260,11 +3677,7 @@ describe('given the load organizations connection function', () => { } await connectionLoader({ ...connectionArgs }) } catch (err) { - expect(err).toEqual( - new Error( - 'Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.', - ), - ) + expect(err).toEqual(new Error('Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.')) } expect(consoleOutput).toEqual([ @@ -4298,9 +3711,7 @@ describe('given the load organizations connection function', () => { }) } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.', - ), + new Error('Impossible de charger le(s) organisme(s) vérifié(s). Veuillez réessayer.'), ) } diff --git a/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-key.test.js b/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-key.test.js index fdd438d60..eb2a3dd12 100644 --- a/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-key.test.js +++ b/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-key.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,7 +10,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadVerifiedOrgByKey dataloader', () => { - let query, drop, truncate, collections, i18n + let query, drop, truncate, collections, db, i18n const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -22,32 +23,24 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) beforeEach(async () => { - await collections.organizations.save({ + const org1 = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, + policies: { + psd: false, + pgs: false, }, orgDetails: { en: { @@ -72,19 +65,25 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { }, }, }) - await collections.organizations.save({ + const summary1 = await collections.organizationSummaries.save({ + organization: org1._id, + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, + }) + await db.collection('organizations').update(org1._key, { latestSummaryId: summary1._id }) + const org2 = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, + policies: { + psd: false, + pgs: false, }, orgDetails: { en: { @@ -109,6 +108,20 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { }, }, }) + const summary2 = await collections.organizationSummaries.save({ + organization: org2._id, + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, + }) + await db.collection('organizations').update(org2._key, { latestSummaryId: summary2._id }) }) afterEach(async () => { await truncate() @@ -138,7 +151,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { FOR org IN organizations FILTER org.orgDetails.en.slug == "communications-security-establishment" LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("en", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: { psd: false, pgs: false }, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("en", org.orgDetails)) ` const expectedOrg = await expectedCursor.next() @@ -155,7 +168,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { const expectedCursor = await query` FOR org IN organizations LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("en", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("en", org.orgDetails)) ` while (expectedCursor.hasMore) { @@ -192,7 +205,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { FOR org IN organizations FILTER org.orgDetails.fr.slug == "centre-de-la-securite-des-telecommunications" LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("fr", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("fr", org.orgDetails)) ` const expectedOrg = await expectedCursor.next() @@ -209,7 +222,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { const expectedCursor = await query` FOR org IN organizations LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("fr", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("fr", org.orgDetails)) ` while (expectedCursor.hasMore) { @@ -243,9 +256,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedOrgByKey({ query: mockedQuery, language: 'en', @@ -255,11 +266,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to find verified organization(s). Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to find verified organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -284,11 +291,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { try { await loader.load('1') } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to find verified organization(s). Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to find verified organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -314,9 +317,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedOrgByKey({ query: mockedQuery, language: 'fr', @@ -327,9 +328,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { await loader.load('1') } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.', - ), + new Error('Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.'), ) } @@ -356,9 +355,7 @@ describe('given a loadVerifiedOrgByKey dataloader', () => { await loader.load('1') } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.', - ), + new Error('Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.'), ) } diff --git a/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-slug.test.js b/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-slug.test.js index e67fdbe77..025ca3b7a 100644 --- a/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-slug.test.js +++ b/api/src/verified-organizations/loaders/__tests__/load-verified-organization-by-slug.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { setupI18n } from '@lingui/core' import englishMessages from '../../../locale/en/messages' @@ -9,7 +10,7 @@ import dbschema from '../../../../database.json' const { DB_PASS: rootPass, DB_URL: url } = process.env describe('given a loadVerifiedOrgBySlug dataloader', () => { - let query, drop, truncate, collections, i18n + let query, drop, truncate, collections, db, i18n const consoleOutput = [] const mockedError = (output) => consoleOutput.push(output) @@ -22,32 +23,24 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { describe('given a successful load', () => { beforeAll(async () => { - ;({ query, drop, truncate, collections } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, + ;({ query, drop, truncate, collections, db } = await ensure({ + variables: { + dbname: dbNameFromFile(__filename), + username: 'root', + rootPassword: rootPass, + password: rootPass, + url, + }, - schema: dbschema, - })) + schema: dbschema, + })) }) beforeEach(async () => { - await collections.organizations.save({ + const org1 = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, + policies: { + psd: false, + pgs: false, }, orgDetails: { en: { @@ -72,19 +65,25 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { }, }, }) - await collections.organizations.save({ + const summary1 = await collections.organizationSummaries.save({ + organization: org1._id, + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, + }) + await db.collection('organizations').update(org1._key, { latestSummaryId: summary1._id }) + const org2 = await collections.organizations.save({ verified: true, - summaries: { - web: { - pass: 50, - fail: 1000, - total: 1050, - }, - mail: { - pass: 50, - fail: 1000, - total: 1050, - }, + policies: { + psd: false, + pgs: false, }, orgDetails: { en: { @@ -109,6 +108,20 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { }, }, }) + const summary2 = await collections.organizationSummaries.save({ + organization: org2._id, + web: { + pass: 50, + fail: 1000, + total: 1050, + }, + mail: { + pass: 50, + fail: 1000, + total: 1050, + }, + }) + await db.collection('organizations').update(org2._key, { latestSummaryId: summary2._id }) }) afterEach(async () => { await truncate() @@ -138,7 +151,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { FOR org IN organizations FILTER org.orgDetails.en.slug == "communications-security-establishment" LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("en", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("en", org.orgDetails)) ` const expectedOrg = await expectedCursor.next() @@ -155,7 +168,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { const expectedCursor = await query` FOR org IN organizations LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("en", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("en", org.orgDetails)) ` while (expectedCursor.hasMore) { @@ -192,7 +205,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { FOR org IN organizations FILTER org.orgDetails.fr.slug == "centre-de-la-securite-des-telecommunications" LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("fr", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("fr", org.orgDetails)) ` const expectedOrg = await expectedCursor.next() @@ -209,7 +222,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { const expectedCursor = await query` FOR org IN organizations LET domains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(domains), summaries: org.summaries }, TRANSLATE("fr", org.orgDetails)) + RETURN MERGE({ _id: org._id, _key: org._key, id: org._key, _rev: org._rev, _type: "verifiedOrganization", verified: org.verified, policies: org.policies, domainCount: COUNT(domains), summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE("fr", org.orgDetails)) ` while (expectedCursor.hasMore) { @@ -243,9 +256,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedOrgBySlug({ query: mockedQuery, language: 'en', @@ -255,11 +266,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { try { await loader.load('slug') } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to find verified organization(s). Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to find verified organization(s). Please try again.')) } expect(consoleOutput).toEqual([ @@ -284,16 +291,10 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { try { await loader.load('slug') } catch (err) { - expect(err).toEqual( - new Error( - 'Unable to find verified organization(s). Please try again.', - ), - ) + expect(err).toEqual(new Error('Unable to find verified organization(s). Please try again.')) } - expect(consoleOutput).toEqual([ - `Cursor error during loadVerifiedOrgBySlug: Error: Cursor error occurred.`, - ]) + expect(consoleOutput).toEqual([`Cursor error during loadVerifiedOrgBySlug: Error: Cursor error occurred.`]) }) }) }) @@ -314,9 +315,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { }) describe('database error is raised', () => { it('returns an error', async () => { - const mockedQuery = jest - .fn() - .mockRejectedValue(new Error('Database error occurred.')) + const mockedQuery = jest.fn().mockRejectedValue(new Error('Database error occurred.')) const loader = loadVerifiedOrgBySlug({ query: mockedQuery, language: 'fr', @@ -327,9 +326,7 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { await loader.load('slug') } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.', - ), + new Error('Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.'), ) } @@ -356,15 +353,11 @@ describe('given a loadVerifiedOrgBySlug dataloader', () => { await loader.load('slug') } catch (err) { expect(err).toEqual( - new Error( - 'Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.', - ), + new Error('Impossible de trouver une ou plusieurs organisations vérifiées. Veuillez réessayer.'), ) } - expect(consoleOutput).toEqual([ - `Cursor error during loadVerifiedOrgBySlug: Error: Cursor error occurred.`, - ]) + expect(consoleOutput).toEqual([`Cursor error during loadVerifiedOrgBySlug: Error: Cursor error occurred.`]) }) }) }) diff --git a/api/src/verified-organizations/loaders/load-verified-organization-by-key.js b/api/src/verified-organizations/loaders/load-verified-organization-by-key.js index 225af1326..161ef72d5 100644 --- a/api/src/verified-organizations/loaders/load-verified-organization-by-key.js +++ b/api/src/verified-organizations/loaders/load-verified-organization-by-key.js @@ -21,8 +21,9 @@ export const loadVerifiedOrgByKey = ({ query, language, i18n }) => _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(orgDomains), - summaries: org.summaries - }, + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null + }, TRANSLATE(${language}, org.orgDetails) ) ` diff --git a/api/src/verified-organizations/loaders/load-verified-organization-by-slug.js b/api/src/verified-organizations/loaders/load-verified-organization-by-slug.js index fc889451c..da45a45f0 100644 --- a/api/src/verified-organizations/loaders/load-verified-organization-by-slug.js +++ b/api/src/verified-organizations/loaders/load-verified-organization-by-slug.js @@ -21,8 +21,9 @@ export const loadVerifiedOrgBySlug = ({ query, language, i18n }) => _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(orgDomains), - summaries: org.summaries - }, + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null + }, TRANSLATE(${language}, org.orgDetails) ) ` diff --git a/api/src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js b/api/src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js index 7ea69ddce..8f9f39415 100644 --- a/api/src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js +++ b/api/src/verified-organizations/loaders/load-verified-organization-connections-by-domain-id.js @@ -50,23 +50,23 @@ export const loadVerifiedOrgConnectionsByDomainId = documentField = aql`afterVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`afterVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`afterVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`afterVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`afterVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`afterVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`afterVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND afterVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -127,23 +127,23 @@ export const loadVerifiedOrgConnectionsByDomainId = documentField = aql`beforeVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`beforeVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`beforeVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`beforeVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`beforeVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`beforeVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`beforeVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND beforeVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -266,27 +266,27 @@ export const loadVerifiedOrgConnectionsByDomainId = hasNextPageDocumentField = aql`LAST(retrievedOrgs).verified` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).verified` } else if (orderBy.field === 'summary-mail-pass') { - orgField = aql`org.summaries.mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - orgField = aql`org.summaries.mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.fail` } else if (orderBy.field === 'summary-mail-total') { - orgField = aql`org.summaries.mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.total` } else if (orderBy.field === 'summary-web-pass') { - orgField = aql`org.summaries.web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.pass` } else if (orderBy.field === 'summary-web-fail') { - orgField = aql`org.summaries.web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.fail` } else if (orderBy.field === 'summary-web-total') { - orgField = aql`org.summaries.web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.total` } else if (orderBy.field === 'domain-count') { @@ -329,17 +329,17 @@ export const loadVerifiedOrgConnectionsByDomainId = } else if (orderBy.field === 'verified') { sortByField = aql`org.verified ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-pass') { - sortByField = aql`org.summaries.mail.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-fail') { - sortByField = aql`org.summaries.mail.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-total') { - sortByField = aql`org.summaries.mail.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.total ${orderBy.direction},` } else if (orderBy.field === 'summary-web-pass') { - sortByField = aql`org.summaries.web.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-web-fail') { - sortByField = aql`org.summaries.web.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-web-total') { - sortByField = aql`org.summaries.web.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.total ${orderBy.direction},` } else if (orderBy.field === 'domain-count') { sortByField = aql`COUNT(domains) ${orderBy.direction},` } @@ -359,7 +359,7 @@ export const loadVerifiedOrgConnectionsByDomainId = LET verifiedOrgs = FLATTEN( FOR v, e IN INBOUND ${domainId} claims FILTER v.verified == true RETURN v._key ) - + ${afterVar} ${beforeVar} @@ -367,13 +367,13 @@ export const loadVerifiedOrgConnectionsByDomainId = FOR org IN organizations FILTER org._key IN verifiedOrgs LET orgDomains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - ${afterTemplate} + ${afterTemplate} ${beforeTemplate} SORT ${sortByField} ${limitTemplate} RETURN MERGE( - { + { _id: org._id, _key: org._key, id: org._key, @@ -381,7 +381,8 @@ export const loadVerifiedOrgConnectionsByDomainId = _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(orgDomains), - summaries: org.summaries + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null }, TRANSLATE(${language}, org.orgDetails) ) @@ -395,7 +396,7 @@ export const loadVerifiedOrgConnectionsByDomainId = SORT ${sortByField} TO_NUMBER(org._key) ${sortString} LIMIT 1 RETURN org ) > 0 ? true : false) - + LET hasPreviousPage = (LENGTH( FOR org IN organizations FILTER org._key IN verifiedOrgs @@ -404,15 +405,15 @@ export const loadVerifiedOrgConnectionsByDomainId = SORT ${sortByField} TO_NUMBER(org._key) ${sortString} LIMIT 1 RETURN org ) > 0 ? true : false) - - RETURN { + + RETURN { "verifiedOrgs": verifiedOrgs, "organizations": retrievedOrgs, "totalCount": LENGTH(verifiedOrgs), - "hasNextPage": hasNextPage, - "hasPreviousPage": hasPreviousPage, - "startKey": FIRST(retrievedOrgs)._key, - "endKey": LAST(retrievedOrgs)._key + "hasNextPage": hasNextPage, + "hasPreviousPage": hasPreviousPage, + "startKey": FIRST(retrievedOrgs)._key, + "endKey": LAST(retrievedOrgs)._key } ` } catch (err) { diff --git a/api/src/verified-organizations/loaders/load-verified-organizations-connections.js b/api/src/verified-organizations/loaders/load-verified-organizations-connections.js index 2753c388c..5891d0f52 100644 --- a/api/src/verified-organizations/loaders/load-verified-organizations-connections.js +++ b/api/src/verified-organizations/loaders/load-verified-organizations-connections.js @@ -50,23 +50,23 @@ export const loadVerifiedOrgConnections = documentField = aql`afterVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`afterVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`afterVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`afterVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`afterVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`afterVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`afterVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(afterVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND afterVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -125,23 +125,23 @@ export const loadVerifiedOrgConnections = documentField = aql`beforeVar.verified` orgField = aql`org.verified` } else if (orderBy.field === 'summary-mail-pass') { - documentField = aql`beforeVar.summaries.mail.pass` - orgField = aql`org.summaries.mail.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - documentField = aql`beforeVar.summaries.mail.fail` - orgField = aql`org.summaries.mail.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` } else if (orderBy.field === 'summary-mail-total') { - documentField = aql`beforeVar.summaries.mail.total` - orgField = aql`org.summaries.mail.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` } else if (orderBy.field === 'summary-web-pass') { - documentField = aql`beforeVar.summaries.web.pass` - orgField = aql`org.summaries.web.pass` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` } else if (orderBy.field === 'summary-web-fail') { - documentField = aql`beforeVar.summaries.web.fail` - orgField = aql`org.summaries.web.fail` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` } else if (orderBy.field === 'summary-web-total') { - documentField = aql`beforeVar.summaries.web.total` - orgField = aql`org.summaries.web.total` + documentField = aql`DOCUMENT(beforeVar.latestSummaryId).web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` } else if (orderBy.field === 'domain-count') { documentField = aql`COUNT(FOR v, e IN 1..1 OUTBOUND beforeVar._id claims RETURN e._to)` orgField = aql`COUNT(orgDomains)` @@ -264,27 +264,27 @@ export const loadVerifiedOrgConnections = hasNextPageDocumentField = aql`LAST(retrievedOrgs).verified` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).verified` } else if (orderBy.field === 'summary-mail-pass') { - orgField = aql`org.summaries.mail.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.pass` } else if (orderBy.field === 'summary-mail-fail') { - orgField = aql`org.summaries.mail.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.fail` } else if (orderBy.field === 'summary-mail-total') { - orgField = aql`org.summaries.mail.total` + orgField = aql`DOCUMENT(org.latestSummaryId).mail.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.mail.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.mail.total` } else if (orderBy.field === 'summary-web-pass') { - orgField = aql`org.summaries.web.pass` + orgField = aql`DOCUMENT(org.latestSummaryId).web.pass` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.pass` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.pass` } else if (orderBy.field === 'summary-web-fail') { - orgField = aql`org.summaries.web.fail` + orgField = aql`DOCUMENT(org.latestSummaryId).web.fail` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.fail` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.fail` } else if (orderBy.field === 'summary-web-total') { - orgField = aql`org.summaries.web.total` + orgField = aql`DOCUMENT(org.latestSummaryId).web.total` hasNextPageDocumentField = aql`LAST(retrievedOrgs).summaries.web.total` hasPreviousPageDocumentField = aql`FIRST(retrievedOrgs).summaries.web.total` } else if (orderBy.field === 'domain-count') { @@ -327,17 +327,17 @@ export const loadVerifiedOrgConnections = } else if (orderBy.field === 'verified') { sortByField = aql`org.verified ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-pass') { - sortByField = aql`org.summaries.mail.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-fail') { - sortByField = aql`org.summaries.mail.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-mail-total') { - sortByField = aql`org.summaries.mail.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).mail.total ${orderBy.direction},` } else if (orderBy.field === 'summary-web-pass') { - sortByField = aql`org.summaries.web.pass ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.pass ${orderBy.direction},` } else if (orderBy.field === 'summary-web-fail') { - sortByField = aql`org.summaries.web.fail ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.fail ${orderBy.direction},` } else if (orderBy.field === 'summary-web-total') { - sortByField = aql`org.summaries.web.total ${orderBy.direction},` + sortByField = aql`DOCUMENT(org.latestSummaryId).web.total ${orderBy.direction},` } else if (orderBy.field === 'domain-count') { sortByField = aql`COUNT(domains) ${orderBy.direction},` } @@ -367,7 +367,7 @@ export const loadVerifiedOrgConnections = FOR org IN organizations FILTER org._key IN verifiedOrgs LET orgDomains = (FOR v, e IN 1..1 OUTBOUND org._id claims RETURN e._to) - ${afterTemplate} + ${afterTemplate} ${beforeTemplate} SORT ${sortByField} @@ -381,8 +381,9 @@ export const loadVerifiedOrgConnections = _type: "verifiedOrganization", verified: org.verified, domainCount: COUNT(orgDomains), - summaries: org.summaries - }, + policies: org.policies, + summaries: org.latestSummaryId ? DOCUMENT(org.latestSummaryId) : null + }, TRANSLATE(${language}, org.orgDetails) ) ) @@ -395,7 +396,7 @@ export const loadVerifiedOrgConnections = SORT ${sortByField} TO_NUMBER(org._key) ${sortString} LIMIT 1 RETURN org ) > 0 ? true : false) - + LET hasPreviousPage = (LENGTH( FOR org IN organizations FILTER org._key IN verifiedOrgs @@ -404,14 +405,14 @@ export const loadVerifiedOrgConnections = SORT ${sortByField} TO_NUMBER(org._key) ${sortString} LIMIT 1 RETURN org ) > 0 ? true : false) - - RETURN { + + RETURN { "organizations": retrievedOrgs, "totalCount": LENGTH(verifiedOrgs), - "hasNextPage": hasNextPage, - "hasPreviousPage": hasPreviousPage, - "startKey": FIRST(retrievedOrgs)._key, - "endKey": LAST(retrievedOrgs)._key + "hasNextPage": hasNextPage, + "hasPreviousPage": hasPreviousPage, + "startKey": FIRST(retrievedOrgs)._key, + "endKey": LAST(retrievedOrgs)._key } ` } catch (err) { diff --git a/api/src/verified-organizations/queries/__tests__/find-verified-organization-by-slug.test.js b/api/src/verified-organizations/queries/__tests__/find-verified-organization-by-slug.test.js index 43e8c0f8d..c994d08ea 100644 --- a/api/src/verified-organizations/queries/__tests__/find-verified-organization-by-slug.test.js +++ b/api/src/verified-organizations/queries/__tests__/find-verified-organization-by-slug.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema, GraphQLError } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' diff --git a/api/src/verified-organizations/queries/__tests__/find-verified-organizations.test.js b/api/src/verified-organizations/queries/__tests__/find-verified-organizations.test.js index da498685b..2b212ac3d 100644 --- a/api/src/verified-organizations/queries/__tests__/find-verified-organizations.test.js +++ b/api/src/verified-organizations/queries/__tests__/find-verified-organizations.test.js @@ -1,4 +1,5 @@ -import { ensure, dbNameFromFile } from 'arango-tools' +import { dbNameFromFile } from 'arango-tools' +import { ensureDatabase as ensure } from '../../../testUtilities' import { graphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { setupI18n } from '@lingui/core' diff --git a/api/src/web-scan/data-source.js b/api/src/web-scan/data-source.js new file mode 100644 index 000000000..a7abd16a9 --- /dev/null +++ b/api/src/web-scan/data-source.js @@ -0,0 +1,8 @@ +import { loadWebConnectionsByDomainId, loadWebScansByWebId } from './loaders' + +export class WebScanDataSource { + constructor({ query, userKey, cleanseInput, i18n }) { + this.getConnectionsByDomainId = loadWebConnectionsByDomainId({ query, userKey, cleanseInput, i18n }) + this.getScansByWebId = loadWebScansByWebId({ query, userKey, cleanseInput, i18n }) + } +} diff --git a/api/src/web-scan/index.js b/api/src/web-scan/index.js index 84c283025..d1175cffd 100644 --- a/api/src/web-scan/index.js +++ b/api/src/web-scan/index.js @@ -1,2 +1,3 @@ export * from './loaders' export * from './objects' +export * from './data-source' diff --git a/api/src/web-scan/objects/tls-result.js b/api/src/web-scan/objects/tls-result.js index 40772d91b..78e1d541b 100644 --- a/api/src/web-scan/objects/tls-result.js +++ b/api/src/web-scan/objects/tls-result.js @@ -53,22 +53,22 @@ export const tlsResultType = new GraphQLObjectType({ positiveTags: { type: new GraphQLList(guidanceTagType), description: `List of positive tags for the scanned server from this scan.`, - resolve: async ({ positiveTags }, _, { loaders: { loadSslGuidanceTagByTagId } }) => { - return await loadSslGuidanceTagByTagId({ tags: positiveTags }) + resolve: async ({ positiveTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: positiveTags }) }, }, neutralTags: { type: new GraphQLList(guidanceTagType), description: `List of neutral tags for the scanned server from this scan.`, - resolve: async ({ neutralTags }, _, { loaders: { loadSslGuidanceTagByTagId } }) => { - return await loadSslGuidanceTagByTagId({ tags: neutralTags }) + resolve: async ({ neutralTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: neutralTags }) }, }, negativeTags: { type: new GraphQLList(guidanceTagType), description: `List of negative tags for the scanned server from this scan.`, - resolve: async ({ negativeTags }, _, { loaders: { loadSslGuidanceTagByTagId } }) => { - return await loadSslGuidanceTagByTagId({ tags: negativeTags }) + resolve: async ({ negativeTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: negativeTags }) }, }, certificateStatus: { @@ -247,6 +247,10 @@ export const certificateChainInfoType = new GraphQLObjectType({ type: GraphQLBoolean, description: `Whether or not the certificate chain passed validation.`, }, + hasEntrustCertificate: { + type: GraphQLBoolean, + description: `Whether or not the certificate chain contains an Entrust certificate.`, + }, }), }) diff --git a/api/src/web-scan/objects/web-connection-result.js b/api/src/web-scan/objects/web-connection-result.js index 99f0ff290..d043e081d 100644 --- a/api/src/web-scan/objects/web-connection-result.js +++ b/api/src/web-scan/objects/web-connection-result.js @@ -68,22 +68,22 @@ export const webConnectionResultType = new GraphQLObjectType({ positiveTags: { type: new GraphQLList(guidanceTagType), description: `List of positive tags for the scanned server from this scan.`, - resolve: async ({ positiveTags }, _, { loaders: { loadHttpsGuidanceTagByTagId } }) => { - return await loadHttpsGuidanceTagByTagId({ tags: positiveTags }) + resolve: async ({ positiveTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: positiveTags }) }, }, neutralTags: { type: new GraphQLList(guidanceTagType), description: `List of neutral tags for the scanned server from this scan.`, - resolve: async ({ neutralTags }, _, { loaders: { loadHttpsGuidanceTagByTagId } }) => { - return await loadHttpsGuidanceTagByTagId({ tags: neutralTags }) + resolve: async ({ neutralTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: neutralTags }) }, }, negativeTags: { type: new GraphQLList(guidanceTagType), description: `List of negative tags for the scanned server from this scan.`, - resolve: async ({ negativeTags }, _, { loaders: { loadHttpsGuidanceTagByTagId } }) => { - return await loadHttpsGuidanceTagByTagId({ tags: negativeTags }) + resolve: async ({ negativeTags }, _, { dataSources: { guidanceTag } }) => { + return await guidanceTag.byTagId({ tags: negativeTags }) }, }, }), @@ -183,5 +183,57 @@ export const connectionChainResultType = new GraphQLObjectType({ type: new GraphQLList(connectionType), description: `The connection chain created when following redirects.`, }, + securityTxt: { + type: new GraphQLList(securityTxtResultType), + description: 'Result of fetching and parsing the security.txt file for this domain.', + }, + }), +}) + +export const securityTxtResultType = new GraphQLObjectType({ + name: 'SecurityTxtResult', + description: 'Represents the result of a security.txt file fetch and parse operation.', + fields: () => ({ + path: { + type: GraphQLString, + description: + 'The path where the security.txt file was requested (e.g., /.well-known/security.txt or /security.txt).', + }, + url: { + type: GraphQLString, + description: 'The full URL used to fetch the security.txt file.', + }, + statusCode: { + type: GraphQLInt, + description: 'The HTTP status code returned when requesting the security.txt file.', + }, + fields: { + type: GraphQLJSONObject, + description: 'Parsed fields from the security.txt file as key-value pairs.', + }, + isValid: { + type: GraphQLBoolean, + description: 'Whether the security.txt file was found and successfully parsed.', + }, + error: { + type: GraphQLString, + description: 'Any errors encountered during fetching or parsing the security.txt file.', + }, + raw: { + type: GraphQLString, + description: 'The raw contents of the security.txt file, if available.', + }, + redirected: { + type: GraphQLBoolean, + description: 'Whether the request for security.txt was redirected.', + }, + redirectLocation: { + type: GraphQLString, + description: 'The location to which the request was redirected, if any.', + }, + redirectStatusCode: { + type: GraphQLInt, + description: 'The HTTP status code returned by any redirect encountered.', + }, }), }) diff --git a/api/src/web-scan/objects/web-scan.js b/api/src/web-scan/objects/web-scan.js index e1e55d6ab..8bf70f124 100644 --- a/api/src/web-scan/objects/web-scan.js +++ b/api/src/web-scan/objects/web-scan.js @@ -1,4 +1,4 @@ -import { GraphQLObjectType, GraphQLString } from 'graphql' +import { GraphQLBoolean, GraphQLObjectType, GraphQLString } from 'graphql' import { webScanResultType } from './web-scan-result' @@ -13,6 +13,10 @@ export const webScanType = new GraphQLObjectType({ type: GraphQLString, description: `The status of the scan for the given domain and IP address.`, }, + isPrivateIp: { + type: GraphQLBoolean, + description: `Whether the IP address is a private IP address.`, + }, results: { type: webScanResultType, description: `Results of TLS and HTTP connection scans on the given domain.`, diff --git a/api/src/web-scan/objects/web.js b/api/src/web-scan/objects/web.js index 77ec486e7..10e1ba540 100644 --- a/api/src/web-scan/objects/web.js +++ b/api/src/web-scan/objects/web.js @@ -21,8 +21,8 @@ export const webType = new GraphQLObjectType({ results: { type: new GraphQLList(webScanType), description: `Results of the web scan at each IP address.`, - resolve: async ({ _id }, args, { loaders: { loadWebScansByWebId } }) => { - return await loadWebScansByWebId({ + resolve: async ({ _id }, args, { dataSources: { webScan } }) => { + return await webScan.getScansByWebId({ webId: _id, ...args, }) diff --git a/api/test-docker-compose.yaml b/api/test-docker-compose.yaml new file mode 100644 index 000000000..91fa12779 --- /dev/null +++ b/api/test-docker-compose.yaml @@ -0,0 +1,20 @@ +version: '3' + +services: + nats-test: + image: nats:2.9.11-scratch + container_name: nats-test + command: -js + restart: always + ports: + - "14222:4222" + + arangodb-test: + image: arangodb:3.12.1 + container_name: arangodb-test + environment: + - ARANGO_ROOT_PASSWORD=test + + restart: unless-stopped + ports: + - "18529:8529" diff --git a/azure-defender-easm/README.md b/azure-defender-easm/README.md new file mode 100644 index 000000000..b41c60e8a --- /dev/null +++ b/azure-defender-easm/README.md @@ -0,0 +1,12 @@ +# Azure EASM + +This is an optional service that links Tracker with the [Microsoft Defender External Attack Surface Management (EASM)](https://learn.microsoft.com/en-us/azure/external-attack-surface-management/). This allows users and service admins to gain new insight into their assets, including asset discovery and CVE detection. + +## Running it + +``` +pipenv install +pipenv run service +``` + +## TODO diff --git a/azure-defender-easm/add-domain-to-easm/.dockerignore b/azure-defender-easm/add-domain-to-easm/.dockerignore new file mode 100644 index 000000000..c2440b7a4 --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +tests +*.yaml +**/*.env diff --git a/azure-defender-easm/add-domain-to-easm/.env.example b/azure-defender-easm/add-domain-to-easm/.env.example new file mode 100644 index 000000000..70addcfc5 --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/.env.example @@ -0,0 +1,11 @@ +SUBSCRIPTION_ID= +WORKSPACE_NAME= +RESOURCE_GROUP= +REGION= + +CLIENT_ID= +CLIENT_SECRET= +TENANT_ID= + +KUSTO_CLUSTER= +KUSTO_DATABASE= \ No newline at end of file diff --git a/azure-defender-easm/add-domain-to-easm/Dockerfile b/azure-defender-easm/add-domain-to-easm/Dockerfile new file mode 100644 index 000000000..314f443b1 --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/Dockerfile @@ -0,0 +1,41 @@ +FROM python:3.14.3-alpine AS python-builder + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /working/install + +RUN apk add --no-cache \ + python3 \ + py3-pip \ + py3-setuptools \ + py3-wheel \ + build-base \ + python3-dev + +COPY requirements.txt /requirements.txt +# Install python requirements to /working/install directory for cleaner copy +RUN pip3 install --prefix=/working/install -r /requirements.txt + +#=============================================================================================== +#=============================================================================================== + +FROM python:3.14.3-alpine + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /add-domain-to-easm + +# Copy installed python modules +COPY --from=python-builder /working/install/lib /usr/local/lib + +COPY service.py ./ +COPY clients ./clients + +RUN adduser -D defender +USER defender + +CMD ["python3", "service.py"] diff --git a/azure-defender-easm/add-domain-to-easm/README.md b/azure-defender-easm/add-domain-to-easm/README.md new file mode 100644 index 000000000..b41c60e8a --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/README.md @@ -0,0 +1,12 @@ +# Azure EASM + +This is an optional service that links Tracker with the [Microsoft Defender External Attack Surface Management (EASM)](https://learn.microsoft.com/en-us/azure/external-attack-surface-management/). This allows users and service admins to gain new insight into their assets, including asset discovery and CVE detection. + +## Running it + +``` +pipenv install +pipenv run service +``` + +## TODO diff --git a/api/src/email-scan/objects/dkim.js b/azure-defender-easm/add-domain-to-easm/clients/__init__.py similarity index 100% rename from api/src/email-scan/objects/dkim.js rename to azure-defender-easm/add-domain-to-easm/clients/__init__.py diff --git a/azure-defender-easm/add-domain-to-easm/clients/easm_client.py b/azure-defender-easm/add-domain-to-easm/clients/easm_client.py new file mode 100644 index 000000000..d786242f4 --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/clients/easm_client.py @@ -0,0 +1,53 @@ +import os +import logging +from azure.identity import ClientSecretCredential +from azure.defender.easm import EasmClient +from dotenv import load_dotenv + +load_dotenv() +logger = logging.getLogger(__name__) + +SUB_ID = os.getenv("SUBSCRIPTION_ID") +WORKSPACE_NAME = os.getenv("WORKSPACE_NAME") +RESOURCE_GROUP = os.getenv("RESOURCE_GROUP") +REGION = os.getenv("REGION") +ENDPOINT = f"{REGION}.easm.defender.microsoft.com" + +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +TENANT_ID = os.getenv("TENANT_ID") +CREDENTIAL = ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET) + +EASM_CLIENT = EasmClient(ENDPOINT, RESOURCE_GROUP, SUB_ID, WORKSPACE_NAME, CREDENTIAL) + + +def run_disco_group(group_name): + org_disco_group = get_disco_group(group_name) + if org_disco_group: + logger.info(f"Running discovery group {group_name}") + EASM_CLIENT.discovery_groups.run(group_name) + + +def list_disco_group_runs(group_name): + org_disco_group = get_disco_group(group_name) + if org_disco_group: + return EASM_CLIENT.discovery_groups.list_runs(group_name) + + +def get_disco_group(group_name): + disco_group = EASM_CLIENT.discovery_groups.get(group_name) + return disco_group + + +def create_disco_group(name, assets, frequency=0): + request = { + "description": "Discovery group made for discovering assets for " + name, + "seeds": assets, + "frequencyMilliseconds": frequency, + } + response = EASM_CLIENT.discovery_groups.put(name, request) + return response + + +def delete_disco_group(group_name): + EASM_CLIENT.discovery_groups.delete(group_name) diff --git a/azure-defender-easm/add-domain-to-easm/clients/kusto_client.py b/azure-defender-easm/add-domain-to-easm/clients/kusto_client.py new file mode 100644 index 000000000..187c2bfd3 --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/clients/kusto_client.py @@ -0,0 +1,37 @@ +from azure.kusto.data import KustoClient, KustoConnectionStringBuilder +from azure.kusto.data.helpers import dataframe_from_result_table + +import os +from dotenv import load_dotenv + +load_dotenv() + +KUSTO_CLUSTER = os.getenv("KUSTO_CLUSTER") +REGION = os.getenv("REGION") +KUSTO_DATABASE = os.getenv("KUSTO_DATABASE") +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +TENANT_ID = os.getenv("TENANT_ID") + +KCSB_DATA = KustoConnectionStringBuilder.with_aad_application_key_authentication( + f"https://{KUSTO_CLUSTER}.{REGION}.kusto.windows.net", + CLIENT_ID, + CLIENT_SECRET, + TENANT_ID, +) +KUSTO_CLIENT = KustoClient(KCSB_DATA) + + +def get_host_asset(host_name): + query = f""" + declare query_parameters(hostName:string = '{host_name}'); + EasmHostAsset + | where Host == hostName + | limit 1 + | project Host, AssetUuid + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data diff --git a/azure-defender-easm/add-domain-to-easm/cloudbuild.yaml b/azure-defender-easm/add-domain-to-easm/cloudbuild.yaml new file mode 100644 index 000000000..62ae4ce41 --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/cloudbuild.yaml @@ -0,0 +1,34 @@ +steps: + - name: "gcr.io/cloud-builders/docker" + id: generate-image-name + entrypoint: "bash" + dir: azure-defender-easm/add-domain-to-easm + args: + - "-c" + - | + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/azure-defender-easm/add-domain-to-easm:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename + + - name: "gcr.io/cloud-builders/docker" + id: build + entrypoint: "bash" + dir: azure-defender-easm/add-domain-to-easm + args: + - "-c" + - | + image=$(cat /workspace/imagename) + docker build -t $image . + + - name: "gcr.io/cloud-builders/docker" + id: push-if-master + entrypoint: "bash" + dir: azure-defender-easm/add-domain-to-easm + args: + - "-c" + - | + if [[ "$BRANCH_NAME" == "master" ]] + then + image=$(cat /workspace/imagename) + docker push $image + else + exit 0 + fi diff --git a/azure-defender-easm/add-domain-to-easm/requirements.txt b/azure-defender-easm/add-domain-to-easm/requirements.txt new file mode 100644 index 000000000..bc781deaa --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/requirements.txt @@ -0,0 +1,7 @@ +azure-identity==1.16.1 +azure-defender-easm==1.0.0b1 +azure-kusto-data==4.3.1 +python-dotenv==1.2.2 +nats-py==2.6.0 +numpy==2.4.4 +pandas==3.0.2 \ No newline at end of file diff --git a/azure-defender-easm/add-domain-to-easm/service.py b/azure-defender-easm/add-domain-to-easm/service.py new file mode 100644 index 000000000..a60edc50a --- /dev/null +++ b/azure-defender-easm/add-domain-to-easm/service.py @@ -0,0 +1,130 @@ +import logging +import os +from dataclasses import dataclass + +from dotenv import load_dotenv + +import asyncio +import nats +import json +import signal + +from nats.errors import TimeoutError as NatsTimeoutError +from nats.js import JetStreamContext +from nats.js.api import RetentionPolicy, ConsumerConfig, AckPolicy + +load_dotenv() + +logging.basicConfig( + level=logging.INFO, format="[%(asctime)s :: %(name)s :: %(levelname)s] %(message)s" +) +logger = logging.getLogger(__name__) + +from clients.easm_client import run_disco_group, create_disco_group +from clients.kusto_client import get_host_asset + +NAME = os.getenv("NAME", "add-domain-to-easm") +SERVERLIST = os.getenv("NATS_SERVERS", "nats://localhost:4222") +SERVERS = SERVERLIST.split(",") + + +async def run(): + loop = asyncio.get_running_loop() + + @dataclass + class Context: + should_exit: bool = False + sub: JetStreamContext.PullSubscription = None + + context = Context() + + async def error_cb(error): + logger.error(f"Uncaught error in callback: {error}") + + async def reconnected_cb(): + logger.info(f"Reconnected to NATS at {nc.connected_url.netloc}...") + # Ensure jetstream consumer is still present + context.sub = await js.pull_subscribe(**pull_subscribe_options) + + nc = await nats.connect( + error_cb=error_cb, + reconnected_cb=reconnected_cb, + servers=SERVERS, + name=NAME, + ) + + js = nc.jetstream() + logger.info(f"Connected to NATS at {nc.connected_url.netloc}...") + + pull_subscribe_options = { + "stream": "SCANS", + "subject": "scans.add_domain_to_easm", + "durable": "add_domain_to_easm", + "config": ConsumerConfig( + ack_policy=AckPolicy.EXPLICIT, + max_deliver=1, + max_waiting=100_000, + ack_wait=90, + ), + } + + context.sub = await js.pull_subscribe(**pull_subscribe_options) + + async def ask_exit(sig_name): + if context.should_exit is True: + return + logger.error(f"Got signal {sig_name}: exit") + context.should_exit = True + + for signal_name in {"SIGINT", "SIGTERM"}: + loop.add_signal_handler( + getattr(signal, signal_name), + lambda: asyncio.create_task(ask_exit(signal_name)), + ) + + while True: + if context.should_exit: + break + if nc.is_closed: + logger.error("Connection to NATS is closed") + + try: + logger.debug("Fetching message...") + msgs = await context.sub.fetch(batch=1, timeout=1) + msg = msgs[0] + except NatsTimeoutError: + logger.debug("No messages available...") + continue + + subject = msg.subject + reply = msg.reply + data = msg.data.decode() + logger.info(f"Received a message on '{subject} {reply}': {data}") + payload = json.loads(msg.data) + + domain = payload.get("domain") + if not domain.endswith(".gc.ca") and not domain.endswith(".canada.ca"): + logger.info(f"Skipping '{domain}' as it is not a GC domain.") + return + + try: + easm_asset = get_host_asset(domain) + if len(easm_asset) > 0: + logger.info(f"Skipping '{domain}' as it already exists in EASM.") + return + except Exception as e: + logger.error(f"Checking if asset exists in EASM: {str(e)}") + return + + try: + logger.info(f"Adding '{domain}' to EASM tooling...") + create_disco_group(name=domain, assets=[{"kind": "host", "name": domain}]) + run_disco_group(domain) + logger.info(f"Successfully added '{domain}' to EASM tooling.") + except Exception as e: + logger.error(f"Scanning subdomains: {str(e)}") + return + + +if __name__ == "__main__": + asyncio.run(run()) diff --git a/azure-defender-easm/add-easm-assets-to-tracker/.dockerignore b/azure-defender-easm/add-easm-assets-to-tracker/.dockerignore new file mode 100644 index 000000000..c2440b7a4 --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +tests +*.yaml +**/*.env diff --git a/azure-defender-easm/add-easm-assets-to-tracker/.env.example b/azure-defender-easm/add-easm-assets-to-tracker/.env.example new file mode 100644 index 000000000..9cc79dfe3 --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/.env.example @@ -0,0 +1,17 @@ +CLIENT_ID= +CLIENT_SECRET= +TENANT_ID= + +REGION= +KUSTO_CLUSTER= +KUSTO_DATABASE= + +DB_USER= +DB_PASS= +DB_URL= +DB_NAME= +UNCLAIMED_ID= +SERVICE_ACCOUNT_EMAIL= + +NATS_URL= +PUBLISH_TO= \ No newline at end of file diff --git a/azure-defender-easm/add-easm-assets-to-tracker/Dockerfile b/azure-defender-easm/add-easm-assets-to-tracker/Dockerfile new file mode 100644 index 000000000..007d38947 --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/Dockerfile @@ -0,0 +1,41 @@ +FROM python:3.14.3-alpine AS python-builder + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /working/install + +RUN apk add --no-cache \ + python3 \ + py3-pip \ + py3-setuptools \ + py3-wheel \ + build-base \ + python3-dev + +COPY requirements.txt /requirements.txt +# Install python requirements to /working/install directory for cleaner copy +RUN pip3 install --prefix=/working/install -r /requirements.txt + +#=============================================================================================== +#=============================================================================================== + +FROM python:3.14.3-alpine + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /add-easm-assets-to-tracker + +# Copy installed python modules +COPY --from=python-builder /working/install/lib /usr/local/lib + +COPY service.py ./ +COPY clients ./clients + +RUN adduser -D defender +USER defender + +CMD ["python3", "service.py"] diff --git a/azure-defender-easm/add-easm-assets-to-tracker/README.md b/azure-defender-easm/add-easm-assets-to-tracker/README.md new file mode 100644 index 000000000..b7512a8c2 --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/README.md @@ -0,0 +1,5 @@ +# Azure EASM + +This is an optional service that links Tracker with the [Microsoft Defender External Attack Surface Management (EASM)](https://learn.microsoft.com/en-us/azure/external-attack-surface-management/). This allows users and service admins to gain new insight into their assets, including asset discovery and CVE detection. + +## TODO diff --git a/azure-defender-easm/add-easm-assets-to-tracker/clients/__init__.py b/azure-defender-easm/add-easm-assets-to-tracker/clients/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/azure-defender-easm/add-easm-assets-to-tracker/clients/kusto_client.py b/azure-defender-easm/add-easm-assets-to-tracker/clients/kusto_client.py new file mode 100644 index 000000000..1fd7c205e --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/clients/kusto_client.py @@ -0,0 +1,74 @@ +from azure.kusto.data import KustoClient, KustoConnectionStringBuilder +from azure.kusto.data.helpers import dataframe_from_result_table + +import logging +import os +from dotenv import load_dotenv + +load_dotenv() +logger = logging.getLogger(__name__) + +KUSTO_CLUSTER = os.getenv("KUSTO_CLUSTER") +REGION = os.getenv("REGION") +KUSTO_DATABASE = os.getenv("KUSTO_DATABASE") +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +TENANT_ID = os.getenv("TENANT_ID") + +KCSB_DATA = KustoConnectionStringBuilder.with_aad_application_key_authentication( + f"https://{KUSTO_CLUSTER}.{REGION}.kusto.windows.net", + CLIENT_ID, + CLIENT_SECRET, + TENANT_ID, +) +KUSTO_CLIENT = KustoClient(KCSB_DATA) + + +def get_labelled_org_assets_from_org_key(org_key): + query = f""" + declare query_parameters(orgKey:string = '["{org_key}"]'); + EasmAsset + | where TimeGeneratedValue > ago(24h) + | where AssetLastSeen > ago(30d) + | where AssetType == 'HOST' + | where Labels == orgKey + | where AssetName !startswith '*.' + | join kind=inner EasmHostAsset on AssetName + | where TimeGeneratedValue > ago(24h) + | where Cnames == '[]' + | summarize by AssetName + """ + try: + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return [asset["AssetName"] for asset in data] + except Exception as e: + logger.error(f"Failed to get labelled assets from org key: {e}") + return [] + + +def get_unlabelled_assets(): + query = f""" + EasmAsset + | where TimeGeneratedValue > ago(24h) + | where AssetLastSeen > ago(30d) + | where AssetType == 'HOST' + | where Labels == '[]' + | where AssetName !startswith '*.' + | where AssetName endswith '.gc.ca' or AssetName endswith '.canada.ca' + | join kind=inner EasmHostAsset on AssetName + | where TimeGeneratedValue > ago(24h) + | where Cnames == '[]' + | summarize by AssetName + """ + try: + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return [asset["AssetName"] for asset in data] + except Exception as e: + logger.error(f"Failed to get unlabelled assets: {e}") + return [] diff --git a/azure-defender-easm/add-easm-assets-to-tracker/cloudbuild.yaml b/azure-defender-easm/add-easm-assets-to-tracker/cloudbuild.yaml new file mode 100644 index 000000000..7992fe6a8 --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/cloudbuild.yaml @@ -0,0 +1,34 @@ +steps: + - name: "gcr.io/cloud-builders/docker" + id: generate-image-name + entrypoint: "bash" + dir: azure-defender-easm/add-easm-assets-to-tracker + args: + - "-c" + - | + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/azure-defender-easm/add-easm-assets-to-tracker:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename + + - name: "gcr.io/cloud-builders/docker" + id: build + entrypoint: "bash" + dir: azure-defender-easm/add-easm-assets-to-tracker + args: + - "-c" + - | + image=$(cat /workspace/imagename) + docker build -t $image . + + - name: "gcr.io/cloud-builders/docker" + id: push-if-master + entrypoint: "bash" + dir: azure-defender-easm/add-easm-assets-to-tracker + args: + - "-c" + - | + if [[ "$BRANCH_NAME" == "master" ]] + then + image=$(cat /workspace/imagename) + docker push $image + else + exit 0 + fi diff --git a/azure-defender-easm/add-easm-assets-to-tracker/docker-compose.yaml b/azure-defender-easm/add-easm-assets-to-tracker/docker-compose.yaml new file mode 100644 index 000000000..e8f86e21b --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/docker-compose.yaml @@ -0,0 +1,7 @@ +services: + add-easm-assets-to-tracker: + image: northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/azure-defender-easm/add-easm-assets-to-tracker:dirty-${TAG} + build: ./ + env_file: + - .env + network_mode: "host" diff --git a/azure-defender-easm/add-easm-assets-to-tracker/requirements.txt b/azure-defender-easm/add-easm-assets-to-tracker/requirements.txt new file mode 100644 index 000000000..1508ec177 --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/requirements.txt @@ -0,0 +1,7 @@ +azure-kusto-data==4.3.1 +python-dotenv==1.2.2 +python-arango==8.3.1 +nats-py==2.6.0 +numpy==2.4.4 +pandas==3.0.2 +dnspython==2.6.1 \ No newline at end of file diff --git a/azure-defender-easm/add-easm-assets-to-tracker/service.py b/azure-defender-easm/add-easm-assets-to-tracker/service.py new file mode 100644 index 000000000..e0fdfde1a --- /dev/null +++ b/azure-defender-easm/add-easm-assets-to-tracker/service.py @@ -0,0 +1,347 @@ +import logging +import os +import re +import json +from datetime import datetime, timezone +from arango import ArangoClient + +from dotenv import load_dotenv +import asyncio +import nats + +import dns.resolver +from dns.resolver import NXDOMAIN, NoAnswer, NoNameservers +from dns.exception import Timeout + +load_dotenv() + +logging.basicConfig( + level=logging.INFO, format="[%(asctime)s :: %(name)s :: %(levelname)s] %(message)s" +) +logger = logging.getLogger() + +TIMEOUT = int(os.getenv("SCAN_TIMEOUT", "20")) + +from clients.kusto_client import ( + get_labelled_org_assets_from_org_key, + get_unlabelled_assets, +) + +DB_USER = os.getenv("DB_USER") +DB_PASS = os.getenv("DB_PASS") +DB_NAME = os.getenv("DB_NAME") +DB_URL = os.getenv("DB_URL") + +NATS_URL = os.getenv("NATS_URL") +UNCLAIMED_ID = os.getenv("UNCLAIMED_ID") +SERVICE_ACCOUNT_EMAIL = os.getenv("SERVICE_ACCOUNT_EMAIL") + +# Establish DB connection +arango_client = ArangoClient(hosts=DB_URL) +db = arango_client.db(DB_NAME, username=DB_USER, password=DB_PASS) + + +async def main(): + logger.info("Connecting to NATS") + nc = await nats.connect(NATS_URL) + logger.info("Successfully connected to NATS") + + js = nc.jetstream() + + async def publish(channel, msg): + await js.publish(channel, msg) + + # queries + def get_verified_orgs(): + query = """ + FOR org IN organizations + FILTER org.verified == true + RETURN { "key": org._key, "id": org._id } + """ + try: + cursor = db.aql.execute(query) + logger.info(f"Successfully fetched verified orgs") + return [domain for domain in cursor] + except Exception as e: + logger.error(f"Error occurred when fetching verified orgs: {e}") + return [] + + def get_org_domains(org_id): + query = f""" + FOR v, e IN 1..1 OUTBOUND @org_id claims + RETURN v.domain + """ + cursor = db.aql.execute(query, bind_vars={"org_id": org_id}) + logger.info(f"Successfully fetched domains for org: {org_id}") + return [domain for domain in cursor] + + def get_domain_exists(domain): + query = """ + FOR domain IN domains + FILTER domain.domain == @domain + RETURN domain + """ + bind_vars = {"domain": domain} + try: + cursor = db.aql.execute(query, bind_vars=bind_vars) + domains = [domain for domain in cursor] + return len(domains) > 0 + except Exception as e: + logger.error(f"Error occurred when checking if domain exists: {e}") + return None + + def extract_root_domains(subdomains): + root_domains_set = set() + for subdomain in subdomains: + parts = subdomain.split(".") + if len(parts) > 1: + root_domain = ".".join(parts[-3:]) + match = re.match(r"^([^.]+)\.(gc|canada)\.ca$", root_domain) + if match: + full_root_domain = match.group(0) + root_domains_set.add(full_root_domain) + + unique_root_domains = list(root_domains_set) + return unique_root_domains + + # insert functions + def create_domain(domain: str, txn_col): + insert_domain = { + "domain": domain.lower(), + "lastRan": None, + "status": { + "certificates": "info", + "ciphers": "info", + "curves": "info", + "dkim": "info", + "dmarc": "info", + "hsts": "info", + "https": "info", + "protocols": "info", + "spf": "info", + "ssl": "info", + }, + "archived": False, + "ignoreRua": False, + } + + try: + created_domain = txn_col.insert(insert_domain) + logger.info(f"Successfully created domain: {domain}") + return created_domain + except Exception as e: + logger.error(f"Error occurred when creating domain: {e}") + return None + + def create_claim(org_id, domain_id, domain_name, txn_col): + insert_claim = { + "_from": org_id, + "_to": domain_id, + "tags": ['new-nouveau'], + "assetState": "approved", + "firstSeen": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[ + :-3 + ] + + "Z", + } + + try: + created_claim = txn_col.insert(insert_claim) + logger.info(f"Successfully created claim for domain: {domain_name}") + return created_claim + except Exception as e: + logger.error(f"Error occurred when creating claim for {domain_name}", e) + return None + + def log_activity(domain, org_key, txn_col): + insert_activity = { + "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[ + :-3 + ] + + "Z", + "initiatedBy": { + "id": "easm", + "userName": SERVICE_ACCOUNT_EMAIL, + "role": "service", + }, + "target": { + "resource": domain, + "updatedProperties": [ + { + "name": "tags", + "oldValue": [], + "newValue": ['new-nouveau'], + } + ], + "organization": {"id": org_key}, + "resourceType": "domain", + }, + "action": "add", + "reason": None, + } + + try: + created_log = txn_col.insert(insert_activity) + logger.info(f"Successfully logged activity for domain: {domain}") + return created_log + except Exception as e: + logger.error(f"Error occurred when logging activity for {domain}: {e}") + return None + + # main logic + async def add_discovered_domain(domains, org_id): + for domain in domains: + # check if domain exists in system + domain_exists = get_domain_exists(domain) + if domain_exists is None: + logger.error(f"Error occurred when checking if domain exists: {e}") + continue + # if domain exists, skip + elif domain_exists: + logger.info(f"Domain: {domain} already exists in system") + continue + + # check for NXDOMAIN + resolver = dns.resolver.Resolver() + resolver.timeout = TIMEOUT + resolver.lifetime = TIMEOUT * 2 + try: + resolver.resolve(qname=domain, rdtype=dns.rdatatype.A) + except (NoAnswer, NXDOMAIN, NoNameservers, Timeout): + logger.info(f"Domain: {domain} does not have a valid DNS entry") + continue + except Exception as e: + logger.error( + f"Could not confirm if domain: {domain} has a valid DNS entry" + ) + continue + + # setup transaction + txn_db = db.begin_transaction( + write=[ + db.collection("domains").name, + db.collection("claims").name, + db.collection("auditLogs").name, + ], + ) + txn_col_domains = txn_db.collection("domains") + txn_col_claims = txn_db.collection("claims") + txn_col_audit_logs = txn_db.collection("auditLogs") + + # create domain + created_domain = create_domain(domain=domain, txn_col=txn_col_domains) + if created_domain is None: + # abort transaction + txn_db.abort_transaction() + continue + # add domain to org + created_claim = create_claim( + org_id=org_id, + domain_id=created_domain["_id"], + domain_name=domain, + txn_col=txn_col_claims, + ) + if created_claim is None: + # abort transaction + txn_db.abort_transaction() + continue + # add activity logging + org_key = org_id.split("/")[-1] + created_log = log_activity( + domain=domain, org_key=org_key, txn_col=txn_col_audit_logs + ) + if created_log is None: + # abort transaction + txn_db.abort_transaction() + continue + + # commit transaction + try: + txn_db.commit_transaction() + logger.info(f"Successfully committed transaction for domain: {domain}") + + # publish domain to NATS + try: + await publish( + "scans.requests", + json.dumps( + { + "domain": domain, + "domain_key": created_domain["_key"], + } + ).encode(), + ) + logger.info(f"Published domain: {domain} to NATS") + except Exception as e: + logger.error(f"Failed to publish domain: {domain} to NATS: {e}") + + except Exception as e: + logger.error(f"Failed to commit transaction for domain: {domain}: {e}") + # abort transaction + txn_db.abort_transaction() + continue + + # Get all unlabelled assets once + try: + unlabelled_assets = set(get_unlabelled_assets()) + except Exception as e: + logger.error(f"Error fetching unlabelled assets: {e}") + unlabelled_assets = set() + + org_domain_roots = {} + + verified_orgs = get_verified_orgs() + for org in verified_orgs: + org_key = org["key"] + org_id = org["id"] + + try: + domains = get_org_domains(org_id) + except Exception as e: + logger.error( + f"Error when attempting to fetch domains for org {org_key}: {e}" + ) + continue + + # Extract root domains + try: + unique_roots = extract_root_domains(domains) + org_domain_roots[org_key] = unique_roots + except Exception as e: + logger.error(e) + unique_roots = [] + + try: + labelled_assets = get_labelled_org_assets_from_org_key(org_key) + new_domains = list(set(labelled_assets) - set(domains)) + + for asset in list(unlabelled_assets): + asset_root = extract_root_domains([asset]) + if asset_root and asset_root[0] in unique_roots: + new_domains.append(asset) + unlabelled_assets.discard(asset) + + await add_discovered_domain(new_domains, org_id) + except Exception as e: + logger.error( + f"Error when attempting to add new assets to org {org_key}: {e}" + ) + continue + + # After all orgs, add remaining unlabelled assets to unclaimed org + try: + unclaimed_domains = get_org_domains(UNCLAIMED_ID) + new_domains = list(unlabelled_assets - set(unclaimed_domains)) + await add_discovered_domain(new_domains, UNCLAIMED_ID) + except Exception as e: + logger.error(f"Error when attempting to add new assets to unclaimed org: {e}") + + logger.info("Closing NATS connection") + await nc.close() + logger.info("Successfully closed connection") + + +if __name__ == "__main__": + logger.info("Starting EASM-to-Tracker sync") + asyncio.run(main()) + logger.info(f"EASM-to-Tracker sync shutting down...") diff --git a/azure-defender-easm/import-easm-additional-findings/.dockerignore b/azure-defender-easm/import-easm-additional-findings/.dockerignore new file mode 100644 index 000000000..c2440b7a4 --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +tests +*.yaml +**/*.env diff --git a/azure-defender-easm/import-easm-additional-findings/.env.example b/azure-defender-easm/import-easm-additional-findings/.env.example new file mode 100644 index 000000000..bed1213e8 --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/.env.example @@ -0,0 +1,13 @@ +CLIENT_ID= +CLIENT_SECRET= +TENANT_ID= +CVE_LIST= + +KUSTO_CLUSTER= +KUSTO_DATABASE= +REGION= + +DB_USER= +DB_PASS= +DB_URL= +DB_NAME= \ No newline at end of file diff --git a/azure-defender-easm/import-easm-additional-findings/Dockerfile b/azure-defender-easm/import-easm-additional-findings/Dockerfile new file mode 100644 index 000000000..1eaf2c155 --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/Dockerfile @@ -0,0 +1,41 @@ +FROM python:3.14.3-alpine AS python-builder + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /working/install + +RUN apk add --no-cache \ + python3 \ + py3-pip \ + py3-setuptools \ + py3-wheel \ + build-base \ + python3-dev + +COPY requirements.txt /requirements.txt +# Install python requirements to /working/install directory for cleaner copy +RUN pip3 install --prefix=/working/install -r /requirements.txt + +#=============================================================================================== +#=============================================================================================== + +FROM python:3.14.3-alpine + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /import-easm-additional-findings + +# Copy installed python modules +COPY --from=python-builder /working/install/lib /usr/local/lib + +COPY service.py ./ +COPY clients ./clients + +RUN adduser -D defender +USER defender + +CMD ["python3", "service.py"] diff --git a/azure-defender-easm/import-easm-additional-findings/README.md b/azure-defender-easm/import-easm-additional-findings/README.md new file mode 100644 index 000000000..b7512a8c2 --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/README.md @@ -0,0 +1,5 @@ +# Azure EASM + +This is an optional service that links Tracker with the [Microsoft Defender External Attack Surface Management (EASM)](https://learn.microsoft.com/en-us/azure/external-attack-surface-management/). This allows users and service admins to gain new insight into their assets, including asset discovery and CVE detection. + +## TODO diff --git a/azure-defender-easm/import-easm-additional-findings/clients/__init__.py b/azure-defender-easm/import-easm-additional-findings/clients/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/azure-defender-easm/import-easm-additional-findings/clients/kusto_client.py b/azure-defender-easm/import-easm-additional-findings/clients/kusto_client.py new file mode 100644 index 000000000..d7188964d --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/clients/kusto_client.py @@ -0,0 +1,218 @@ +from azure.kusto.data import KustoClient, KustoConnectionStringBuilder +from azure.kusto.data.helpers import dataframe_from_result_table +from datetime import datetime, date, timedelta +import logging +import os +import requests +import time +from dotenv import load_dotenv + +load_dotenv() +logger = logging.getLogger(__name__) + +KUSTO_CLUSTER = os.getenv("KUSTO_CLUSTER") +REGION = os.getenv("REGION") +KUSTO_DATABASE = os.getenv("KUSTO_DATABASE") +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +TENANT_ID = os.getenv("TENANT_ID") +CVE_LIST = os.getenv("CVE_LIST") + +KCSB_DATA = KustoConnectionStringBuilder.with_aad_application_key_authentication( + f"https://{KUSTO_CLUSTER}.{REGION}.kusto.windows.net", + CLIENT_ID, + CLIENT_SECRET, + TENANT_ID, +) +KUSTO_CLIENT = KustoClient(KCSB_DATA) + + +def filter_recent_data(data_list, last_seen_key, start_date): + try: + return [ + x + for x in data_list + if ( + # There are some strange entries such as port '-1' which don't have a last seen date. Skip these. + x[last_seen_key] + and datetime.strptime(x[last_seen_key].split("T")[0], "%Y-%m-%d").date() + >= start_date + ) + ] + except AttributeError as e: + logger.error( + f"Problem occurred filtering list to recent entries. Returning full list... Error: {e}" + ) + return data_list + + +def get_web_components_by_asset(asset, fetched_cves): + query = f""" + declare query_parameters(asset_name:string = '{asset}'); + EasmAssetWebComponent + | where AssetName == asset_name + | where TimeGeneratedValue > ago(30d) + | where WebComponentLastSeen > ago(30d) + | summarize max_time = max(TimeGeneratedValue) by AssetName + | join kind=inner ( + EasmAssetWebComponent + | where AssetName == asset_name + | where TimeGeneratedValue > ago(30d) + | where WebComponentLastSeen > ago(30d) + ) on $left.max_time == $right.TimeGeneratedValue + | project WebComponentName, WebComponentCategory, WebComponentVersion, WebComponentFirstSeen, WebComponentLastSeen, WebComponentCves, WebComponentPorts + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + + for wc in data: + # format datetime to isoformat + wc["WebComponentFirstSeen"] = wc["WebComponentFirstSeen"].isoformat() + wc["WebComponentLastSeen"] = wc["WebComponentLastSeen"].isoformat() + + # filter cves to only top 25 + top25 = CVE_LIST.split(",") + wc["WebComponentCves"] = [ + cve for cve in wc["WebComponentCves"] if cve["Cve"] in top25 + ] + + component_versions = wc["WebComponentVersion"].split(".", 2) + # Assign confidence levels to each CVE + for cve in wc["WebComponentCves"]: + cve["ConfidenceLevel"] = "unknown" + # if detected version includes patch, high confidence + if len(component_versions) == 3: + cve["ConfidenceLevel"] = "high" + else: + # fetch affected versions of CVE + affected_versions = fetch_cve_affected_versions( + cve["Cve"], wc["WebComponentName"], fetched_cves + ) + for cpe in affected_versions: + try: + start, end = get_version_range(cpe).values() + # compare minor and major version nums + if len(component_versions) == 2: + major = int(component_versions[0]) + minor = int(component_versions[1]) + if start is not None: + if major < int(start.split(".")[0]): + continue + if major > int(end.split(".")[0]): + continue + + if minor < int(end.split(".")[1]): + cve["ConfidenceLevel"] = "high" + elif minor == int(end.split(".")[1]): + cve["ConfidenceLevel"] = "medium" + elif len(component_versions) == 1: + major = int(component_versions[0]) + if start is not None: + if major < int(start.split(".")[0]): + continue + if major < int(end.split(".")[0]): + cve["ConfidenceLevel"] = "high" + elif major == int(end.split(".")[0]): + cve["ConfidenceLevel"] = "low" + except Exception as e: + logger.error( + f"Encountered problem while assigning confidence level for {cve['Cve']} on {asset}: {e}" + ) + continue + + return data + + +def fetch_cve_affected_versions(cve, comp_name, fetched_cves): + try: + return fetched_cves[cve] + except KeyError: + logger.info(f"Data on {cve} has not been fetched yet. Attempting to fetch it.") + + res = {"status_code": 0} + attempts = 1 + while attempts <= 3: + try: + res = requests.get( + f"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve}" + ) + # Check if the response is successful + if res.status_code == 200: + data = res.json() + found = [] + configurations = data["vulnerabilities"][0]["cve"]["configurations"] + for item in configurations: + for node in item["nodes"]: + for cpe in node["cpeMatch"]: + if cpe["criteria"].find(comp_name.lower()) != -1: + found.append(cpe) + fetched_cves[cve] = found + return found + else: + time.sleep(60) + attempts += 1 + except Exception as e: + logger.error(f"Encountered problem while fetching cves for {cve}: {e}") + return None + + # Unable to fetch CVE data + logger.error(f"Unable to fetch data for CVE: {cve}") + fetched_cves[cve] = None + return None + + +def get_version_range(affected_versions): + versions = {"start": None, "end": None} + + for key in ["versionStartExcluding", "versionStartIncluding"]: + if affected_versions.get(key): + versions["start"] = affected_versions[key] + break + + for key in ["versionEndExcluding", "versionEndIncluding"]: + if affected_versions.get(key): + versions["end"] = affected_versions[key] + break + + return versions + + +def get_additional_findings_by_asset(asset): + thirty_days_ago = date.today() - timedelta(days=30) + query = f""" + declare query_parameters(asset_name:string = '{asset}'); + EasmHostAsset + | where AssetName == asset_name + | where TimeGeneratedValue > ago(30d) + | order by TimeGeneratedValue desc + | limit 1 + | project Locations, Ports, Headers + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + result_list = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + + findings = { + "Locations": [], + "Ports": [], + "Headers": [], + } + + if len(result_list) == 0: + return findings + + data = result_list[0] + + findings["Ports"] = filter_recent_data( + data["Ports"], "PortStateLastSeen", thirty_days_ago + ) + findings["Locations"] = filter_recent_data( + data["Locations"], "LastSeen", thirty_days_ago + ) + + findings["Headers"] = data["Headers"] + + return findings diff --git a/azure-defender-easm/import-easm-additional-findings/cloudbuild.yaml b/azure-defender-easm/import-easm-additional-findings/cloudbuild.yaml new file mode 100644 index 000000000..7c663bb50 --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/cloudbuild.yaml @@ -0,0 +1,34 @@ +steps: + - name: "gcr.io/cloud-builders/docker" + id: generate-image-name + entrypoint: "bash" + dir: azure-defender-easm/import-easm-additional-findings + args: + - "-c" + - | + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/azure-defender-easm/import-easm-additional-findings:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename + + - name: "gcr.io/cloud-builders/docker" + id: build + entrypoint: "bash" + dir: azure-defender-easm/import-easm-additional-findings + args: + - "-c" + - | + image=$(cat /workspace/imagename) + docker build -t $image . + + - name: "gcr.io/cloud-builders/docker" + id: push-if-master + entrypoint: "bash" + dir: azure-defender-easm/import-easm-additional-findings + args: + - "-c" + - | + if [[ "$BRANCH_NAME" == "master" ]] + then + image=$(cat /workspace/imagename) + docker push $image + else + exit 0 + fi diff --git a/azure-defender-easm/import-easm-additional-findings/docker-compose.yaml b/azure-defender-easm/import-easm-additional-findings/docker-compose.yaml new file mode 100644 index 000000000..ada7c87b1 --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/docker-compose.yaml @@ -0,0 +1,7 @@ +services: + import-easm-additional-findings: + image: northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/import-easm-additional-findings:dirty-${TAG} + build: ./ + env_file: + - .env + network_mode: "host" diff --git a/azure-defender-easm/import-easm-additional-findings/requirements.txt b/azure-defender-easm/import-easm-additional-findings/requirements.txt new file mode 100644 index 000000000..bf3821bcd --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/requirements.txt @@ -0,0 +1,5 @@ +azure-kusto-data==4.3.1 +python-dotenv==1.2.2 +python-arango==8.3.1 +numpy==2.4.4 +pandas==3.0.2 \ No newline at end of file diff --git a/azure-defender-easm/import-easm-additional-findings/service.py b/azure-defender-easm/import-easm-additional-findings/service.py new file mode 100644 index 000000000..b6ffb4d2d --- /dev/null +++ b/azure-defender-easm/import-easm-additional-findings/service.py @@ -0,0 +1,124 @@ +import json +from datetime import datetime +from arango import ArangoClient +import os +from clients.kusto_client import ( + get_web_components_by_asset, + get_additional_findings_by_asset, +) +from dotenv import load_dotenv +import logging + +load_dotenv() + +DB_USER = os.getenv("DB_USER") +DB_PASS = os.getenv("DB_PASS") +DB_NAME = os.getenv("DB_NAME") +DB_URL = os.getenv("DB_URL") + +logging.basicConfig( + level=logging.INFO, format="[%(asctime)s :: %(name)s :: %(levelname)s] %(message)s" +) +logger = logging.getLogger() + +# Establish DB connection +arango_client = ArangoClient(hosts=DB_URL) +db = arango_client.db(DB_NAME, username=DB_USER, password=DB_PASS) + + +# fetch all domains from the DB +def get_all_domains(): + query = """ + FOR domain IN domains + FILTER domain.archived != True + FILTER domain.rcode != "NXDOMAIN" + RETURN { "domain": domain.domain, "id": domain._id, "key": domain._key, "ignoredCves": domain.ignoredCves || [] } + """ + cursor = db.aql.execute(query) + return [domain for domain in cursor] + + +def upsert_finding(finding): + query = f""" + UPSERT {{ domain: "{finding["domain"]}" }} + INSERT {finding} + UPDATE {finding} + IN additionalFindings + """ + cursor = db.aql.execute(query) + return [domain for domain in cursor] + + +def remove_none_val_in_dict(dict): + new_dict = {} + for k, v in dict: + if v is None: + v = "" + new_dict[k] = v + return new_dict + + +def update_domain_cve_detected(domain, web_components): + cve_detected = False + ignored_cves = domain["ignoredCves"] if domain["ignoredCves"] else [] + for wc in web_components: + non_ignored_cves = [ + cve + for cve in wc["WebComponentCves"] + if cve["Cve"] not in ignored_cves and cve["ConfidenceLevel"] == "high" + ] + if len(non_ignored_cves) > 0: + cve_detected = True + break + query = f""" + UPDATE {{ _key: "{domain["key"]}", cveDetected: {cve_detected} }} IN domains + """ + cursor = db.aql.execute(query) + return [domain for domain in cursor] + + +def main(): + fetched_cves = {} + try: + domains = get_all_domains() + except Exception as e: + logger.error(f"Failed to fetch domains: {e}. Exiting service...") + return + logger.info(f"Successfully fetched {len(domains)} domains") + + for domain in domains: + logger.info(f"Processing domain {domain['domain']}") + try: + web_components = get_web_components_by_asset(domain["domain"], fetched_cves) + additional_findings = get_additional_findings_by_asset(domain["domain"]) + + insert_str = json.dumps( + { + "domain": domain["id"], + "timestamp": datetime.today().isoformat(), + "webComponents": web_components, + "locations": additional_findings["Locations"], + "ports": additional_findings["Ports"], + "headers": additional_findings["Headers"], + } + ) + insert_obj = json.loads( + insert_str, object_pairs_hook=remove_none_val_in_dict + ) + + # insert the findings into the DB + logger.info(f"Upserting additional findings for domain {domain['domain']}") + upsert_finding(insert_obj) + update_domain_cve_detected(domain, web_components) + except Exception as e: + logger.error(f"Failed to process domain {domain['domain']}: {e}") + continue + logger.info( + f"Successfully upserted additional findings for domain {domain['domain']}" + ) + + +if __name__ == "__main__": + logger.info("EASM additional findings import service started") + main() + logger.info(f"EASM additional findings import service shutting down...") diff --git a/azure-defender-easm/label-known-easm-assets/.dockerignore b/azure-defender-easm/label-known-easm-assets/.dockerignore new file mode 100644 index 000000000..c2440b7a4 --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +tests +*.yaml +**/*.env diff --git a/azure-defender-easm/label-known-easm-assets/.env.example b/azure-defender-easm/label-known-easm-assets/.env.example new file mode 100644 index 000000000..e60fb6b53 --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/.env.example @@ -0,0 +1,16 @@ +SUBSCRIPTION_ID= +WORKSPACE_NAME= +RESOURCE_GROUP= +REGION= + +CLIENT_ID= +CLIENT_SECRET= +TENANT_ID= + +KUSTO_CLUSTER= +KUSTO_DATABASE= + +DB_USER= +DB_PASS= +DB_URL= +DB_NAME= \ No newline at end of file diff --git a/azure-defender-easm/label-known-easm-assets/Dockerfile b/azure-defender-easm/label-known-easm-assets/Dockerfile new file mode 100644 index 000000000..a3fa7adf2 --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/Dockerfile @@ -0,0 +1,41 @@ +FROM python:3.14.3-alpine AS python-builder + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /working/install + +RUN apk add --no-cache \ + python3 \ + py3-pip \ + py3-setuptools \ + py3-wheel \ + build-base \ + python3-dev + +COPY requirements.txt /requirements.txt +# Install python requirements to /working/install directory for cleaner copy +RUN pip3 install --prefix=/working/install -r /requirements.txt + +#=============================================================================================== +#=============================================================================================== + +FROM python:3.14.3-alpine + +# Copy local code to the container image. +ENV PYTHONUNBUFFERED 1 +ENV PYTHONWARNINGS ignore + +WORKDIR /label-known-easm-assets + +# Copy installed python modules +COPY --from=python-builder /working/install/lib /usr/local/lib + +COPY service.py ./ +COPY clients ./clients + +RUN adduser -D defender +USER defender + +CMD ["python3", "service.py"] diff --git a/azure-defender-easm/label-known-easm-assets/README.md b/azure-defender-easm/label-known-easm-assets/README.md new file mode 100644 index 000000000..b41c60e8a --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/README.md @@ -0,0 +1,12 @@ +# Azure EASM + +This is an optional service that links Tracker with the [Microsoft Defender External Attack Surface Management (EASM)](https://learn.microsoft.com/en-us/azure/external-attack-surface-management/). This allows users and service admins to gain new insight into their assets, including asset discovery and CVE detection. + +## Running it + +``` +pipenv install +pipenv run service +``` + +## TODO diff --git a/azure-defender-easm/label-known-easm-assets/clients/__init__.py b/azure-defender-easm/label-known-easm-assets/clients/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/azure-defender-easm/label-known-easm-assets/clients/easm_client.py b/azure-defender-easm/label-known-easm-assets/clients/easm_client.py new file mode 100644 index 000000000..c4ce43c7c --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/clients/easm_client.py @@ -0,0 +1,33 @@ +import os +import logging +from azure.identity import ClientSecretCredential +from azure.defender.easm import EasmClient +from dotenv import load_dotenv + +load_dotenv() +logger = logging.getLogger(__name__) + +SUB_ID = os.getenv("SUBSCRIPTION_ID") +WORKSPACE_NAME = os.getenv("WORKSPACE_NAME") +RESOURCE_GROUP = os.getenv("RESOURCE_GROUP") +REGION = os.getenv("REGION") +ENDPOINT = f"{REGION}.easm.defender.microsoft.com" + +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +TENANT_ID = os.getenv("TENANT_ID") +CREDENTIAL = ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET) + +EASM_CLIENT = EasmClient(ENDPOINT, RESOURCE_GROUP, SUB_ID, WORKSPACE_NAME, CREDENTIAL) + + +def label_assets(assets, label): + for asset in assets: + update_request = {"labels": {f"{label}": True}} + asset_filter = f"uuid = {asset['AssetUuid']}" + try: + EASM_CLIENT.assets.update(body=update_request, filter=asset_filter) + logger.info(f"{asset['AssetName']} labeled with {label}") + except Exception as e: + logger.error(f"Failed to label {asset['AssetName']}: {e}") + continue diff --git a/azure-defender-easm/label-known-easm-assets/clients/kusto_client.py b/azure-defender-easm/label-known-easm-assets/clients/kusto_client.py new file mode 100644 index 000000000..fe11fe0e5 --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/clients/kusto_client.py @@ -0,0 +1,68 @@ +from azure.kusto.data import KustoClient, KustoConnectionStringBuilder +from azure.kusto.data.helpers import dataframe_from_result_table + +import logging +import os +from dotenv import load_dotenv + +load_dotenv() +logger = logging.getLogger(__name__) + +KUSTO_CLUSTER = os.getenv("KUSTO_CLUSTER") +REGION = os.getenv("REGION") +KUSTO_DATABASE = os.getenv("KUSTO_DATABASE") +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +TENANT_ID = os.getenv("TENANT_ID") + +KCSB_DATA = KustoConnectionStringBuilder.with_aad_application_key_authentication( + f"https://{KUSTO_CLUSTER}.{REGION}.kusto.windows.net", + CLIENT_ID, + CLIENT_SECRET, + TENANT_ID, +) +KUSTO_CLIENT = KustoClient(KCSB_DATA) + + +def get_unlabelled_org_assets_from_root(root): + query = f""" + declare query_parameters(domainRoot:string = '{root}'); + EasmAsset + | where TimeGeneratedValue > ago(1d) + | where AssetType == 'HOST' + | where AssetName == domainRoot or AssetName endswith strcat('.', domainRoot) + | where Labels == '[]' + | project AssetName, AssetUuid, Labels + | order by AssetName asc + """ + try: + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + except Exception as e: + logger.error(f"Failed to get unlabelled assets from roots: {e}") + return [] + + +def get_unlabelled_org_assets_from_domains(domains): + query = f""" + declare query_parameters(domains:dynamic = dynamic({domains})); + EasmAsset + | where TimeGeneratedValue > ago(1d) + | where AssetType == 'HOST' + | where AssetName in (domains) + | where Labels == '[]' + | project AssetName, AssetUuid, Labels + | order by AssetName asc + """ + try: + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + except Exception as e: + logger.error(f"Failed to get unlabelled assets: {e}") + return [] diff --git a/azure-defender-easm/label-known-easm-assets/cloudbuild.yaml b/azure-defender-easm/label-known-easm-assets/cloudbuild.yaml new file mode 100644 index 000000000..25c39b161 --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/cloudbuild.yaml @@ -0,0 +1,34 @@ +steps: + - name: "gcr.io/cloud-builders/docker" + id: generate-image-name + entrypoint: "bash" + dir: azure-defender-easm/label-known-easm-assets + args: + - "-c" + - | + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/azure-defender-easm/label-known-easm-assets:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename + + - name: "gcr.io/cloud-builders/docker" + id: build + entrypoint: "bash" + dir: azure-defender-easm/label-known-easm-assets + args: + - "-c" + - | + image=$(cat /workspace/imagename) + docker build -t $image . + + - name: "gcr.io/cloud-builders/docker" + id: push-if-master + entrypoint: "bash" + dir: azure-defender-easm/label-known-easm-assets + args: + - "-c" + - | + if [[ "$BRANCH_NAME" == "master" ]] + then + image=$(cat /workspace/imagename) + docker push $image + else + exit 0 + fi diff --git a/azure-defender-easm/label-known-easm-assets/requirements.txt b/azure-defender-easm/label-known-easm-assets/requirements.txt new file mode 100644 index 000000000..d9cf725d4 --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/requirements.txt @@ -0,0 +1,7 @@ +azure-identity==1.16.1 +azure-defender-easm==1.0.0b1 +azure-kusto-data==4.3.1 +python-dotenv==1.2.2 +python-arango==8.3.1 +numpy==2.4.4 +pandas==3.0.2 \ No newline at end of file diff --git a/azure-defender-easm/label-known-easm-assets/service.py b/azure-defender-easm/label-known-easm-assets/service.py new file mode 100644 index 000000000..07d9547f5 --- /dev/null +++ b/azure-defender-easm/label-known-easm-assets/service.py @@ -0,0 +1,125 @@ +import logging +import re +from arango import ArangoClient +import os + +from dotenv import load_dotenv + +load_dotenv() + +logging.basicConfig( + level=logging.INFO, format="[%(asctime)s :: %(name)s :: %(levelname)s] %(message)s" +) +logger = logging.getLogger() + +from clients.kusto_client import ( + get_unlabelled_org_assets_from_root, + get_unlabelled_org_assets_from_domains, +) +from clients.easm_client import label_assets + +DB_USER = os.getenv("DB_USER") +DB_PASS = os.getenv("DB_PASS") +DB_NAME = os.getenv("DB_NAME") +DB_URL = os.getenv("DB_URL") + +UNCLAIMED_ID = os.getenv("UNCLAIMED_ID") + +# Establish DB connection +arango_client = ArangoClient(hosts=DB_URL) +db = arango_client.db(DB_NAME, username=DB_USER, password=DB_PASS) + + +def get_verified_orgs(): + query = """ + FOR org IN organizations + FILTER org.verified == true + FILTER org._key != @unclaimed_id + SORT org.en.name ASC + RETURN { "key": org._key, "id": org._id } + """ + cursor = db.aql.execute(query, bind_vars={"unclaimed_id": UNCLAIMED_ID}) + return [org for org in cursor] + + +def get_org_domains(org_id): + query = f""" + FOR v, e IN 1..1 OUTBOUND @org_id claims + FILTER v.archived != true + FILTER v.rcode != "NXDOMAIN" + RETURN v.domain + """ + cursor = db.aql.execute(query, bind_vars={"org_id": org_id}) + return [domain for domain in cursor] + + +def extract_root_domains(subdomains): + root_domains_set = set() + for subdomain in subdomains: + parts = subdomain.split(".") + if len(parts) > 1: + root_domain = ".".join(parts[-3:]) + match = re.match(r"^([^.]+)\.(gc|canada)\.ca$", root_domain) + if match: + full_root_domain = match.group(0) + root_domains_set.add(full_root_domain) + + unique_root_domains = list(root_domains_set) + return unique_root_domains + + +def update_asset_labels(): + # Get verified org ids + try: + logger.info("Getting verified orgs") + verified_orgs = get_verified_orgs() + logger.info(f"Found {len(verified_orgs)} verified orgs") + except Exception as e: + logger.error(e) + return + for org in verified_orgs: + # Get org domains + try: + logger.info(f"Getting domains for org {org['key']}") + org_domains = get_org_domains(org["id"]) + logger.info(f"Found {len(org_domains)} domains") + except Exception as e: + logger.error(e) + continue + + # label known assets first + try: + logger.info(f"Labeling known assets for org {org['key']}") + known_org_assets = get_unlabelled_org_assets_from_domains(org_domains) + logger.info( + "Found " + str(len(known_org_assets)) + " known unlabelled assets" + ) + label_assets(assets=known_org_assets, label=org["key"]) + except Exception as e: + logger.error(e) + continue + + # Extract root domains + try: + unique_roots = extract_root_domains(org_domains) + except Exception as e: + logger.error(e) + + for root in unique_roots: + try: + logger.info(f"Root domain: {root}") + # get unlabelled assets from roots + unlabelled_org_assets = get_unlabelled_org_assets_from_root(root) + logger.info( + "Found " + str(len(unlabelled_org_assets)) + " unlabelled assets" + ) + label_assets(assets=unlabelled_org_assets, label=org["key"]) + except Exception as e: + logger.error(e) + continue + + +if __name__ == "__main__": + logger.info("EASM label service started") + update_asset_labels() + logger.info(f"EASM label service shutting down...") diff --git a/azure-defender-easm/scripts/.env.example b/azure-defender-easm/scripts/.env.example new file mode 100644 index 000000000..e60fb6b53 --- /dev/null +++ b/azure-defender-easm/scripts/.env.example @@ -0,0 +1,16 @@ +SUBSCRIPTION_ID= +WORKSPACE_NAME= +RESOURCE_GROUP= +REGION= + +CLIENT_ID= +CLIENT_SECRET= +TENANT_ID= + +KUSTO_CLUSTER= +KUSTO_DATABASE= + +DB_USER= +DB_PASS= +DB_URL= +DB_NAME= \ No newline at end of file diff --git a/azure-defender-easm/scripts/ddos-protection-detection.py b/azure-defender-easm/scripts/ddos-protection-detection.py new file mode 100644 index 000000000..c030a173a --- /dev/null +++ b/azure-defender-easm/scripts/ddos-protection-detection.py @@ -0,0 +1,40 @@ +import csv +import time +from kusto_queries import ( + get_hosts_with_ddos_protection, + host_has_ddos_protection, +) + + +def export_to_csv(data, filename): + with open(filename, "w") as csvfile: + fieldnames = ["AssetName", "HasDDOSProtection"] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + + writer.writeheader() + for row in data: + writer.writerow(row) + + +def ddos_protection_detection_service(): + protected_assets = get_hosts_with_ddos_protection() + print(f"Found {len(protected_assets)} host assets") + + export_arr = [] + for host in protected_assets: + ddos_protection = host_has_ddos_protection(host["AssetName"]) + export_arr.append( + { + "AssetName": host["AssetName"], + "HasDDOSProtection": ddos_protection, + } + ) + + current_time = time.strftime("%Y-%m-%d") + export_to_csv(export_arr, f"ddos_protection_{current_time}.csv") + + +if __name__ == "__main__": + print("DDOS Protection Detection Service started") + ddos_protection_detection_service() + print("DDOS Protection Detection Service done") diff --git a/azure-defender-easm/scripts/easm_client.py b/azure-defender-easm/scripts/easm_client.py new file mode 100644 index 000000000..d686664d5 --- /dev/null +++ b/azure-defender-easm/scripts/easm_client.py @@ -0,0 +1,21 @@ +import os +from azure.identity import ClientSecretCredential +from azure.defender.easm import EasmClient +from dotenv import load_dotenv + +load_dotenv() + +sub_id = os.getenv("SUBSCRIPTION_ID") +workspace_name = os.getenv("WORKSPACE_NAME") +resource_group = os.getenv("RESOURCE_GROUP") +region = os.getenv("REGION") +endpoint = f"{region}.easm.defender.microsoft.com" + +client_id = os.getenv("CLIENT_ID") +client_secret = os.getenv("CLIENT_SECRET") +tenant_id = os.getenv("TENANT_ID") +credential = ClientSecretCredential( + client_id=client_id, client_secret=client_secret, tenant_id=tenant_id +) + +EASM_CLIENT = EasmClient(endpoint, resource_group, sub_id, workspace_name, credential) diff --git a/azure-defender-easm/scripts/easm_discover_assets.py b/azure-defender-easm/scripts/easm_discover_assets.py new file mode 100644 index 000000000..6511a37db --- /dev/null +++ b/azure-defender-easm/scripts/easm_discover_assets.py @@ -0,0 +1,37 @@ +from easm_client import EASM_CLIENT + + +def run_disco_group(group_name): + org_disco_group = get_disco_group(group_name) + if org_disco_group: + EASM_CLIENT.discovery_groups.run(group_name) + + +def list_disco_group_runs(group_name): + org_disco_group = get_disco_group(group_name) + if org_disco_group: + return EASM_CLIENT.discovery_groups.list_runs(group_name) + + +def get_disco_group(group_name): + disco_group = EASM_CLIENT.discovery_groups.get(group_name) + return disco_group + + +def create_disco_group(name, assets, frequency=0): + request = { + "description": "Discovery group made for discovering assets for " + name, + "seeds": assets, + "frequencyMilliseconds": frequency, + } + response = EASM_CLIENT.discovery_groups.put(name, request) + return response + + +def delete_disco_group(group_name): + EASM_CLIENT.discovery_groups.delete(group_name) + + +def list_disco_groups(): + for dg in EASM_CLIENT.discovery_groups.list(): + print(f'{dg["id"]}: {dg["name"]}') diff --git a/azure-defender-easm/scripts/easm_get_asset.py b/azure-defender-easm/scripts/easm_get_asset.py new file mode 100644 index 000000000..022401c3e --- /dev/null +++ b/azure-defender-easm/scripts/easm_get_asset.py @@ -0,0 +1,139 @@ +from easm_client import EASM_CLIENT + + +UNNECESSARY_OPEN_PORTS = { + "FTP": [20, 21], + "SSH": [22], + "Telnet": [23], + "SMTP": [25, 587], + "DNS": [53], + "NetBIOS": [137, 139], + "SMB": [445], + "Ports": [1433, 1434, 3306, 465, 55443], + "Remote desktop": [3389], + "MQTT": [1883, 8883], + "AMQP": [5672, 5671], +} + +UNNECESSARY_EXPOSED_HEADERS = [ + "x-aspnet-version", + "server", + "x-powered-by", +] + + +def get_all_asset_cves(asset): + web_components = asset["asset"]["webComponents"] + detected_cves = { + "LOW": [], + "MEDIUM": [], + "HIGH": [], + "CRITICAL": [], + } + for web_component in web_components: + for cve in web_component["cve"]: + severity = cve["cvss3Summary"]["baseSeverity"] + detected_cves[severity].append(cve["name"]) + + for k, v in detected_cves.items(): + v = list(set(v)) + v.sort(reverse=True) + print(f"{k}: {v}") + + return detected_cves + + +def get_web_components(asset): + for wc in asset["asset"]["webComponents"]: + print(wc["type"] + ":", wc["name"]) + return asset["asset"]["webComponents"] + + +def get_cookies(asset): + return asset["asset"]["cookies"] + + +def get_asset(asset_id): + return EASM_CLIENT.assets.get(asset_id=asset_id) + + +def get_vulnerable_web_components(asset): + vulnerable_web_components = [] + web_components = get_web_components(asset) + for wc in web_components: + if len(wc["cve"]) > 0: + print(wc["type"] + ":", wc["name"]) + vulnerable_web_components.append(wc) + return vulnerable_web_components + + +def get_all_unique_cves(vuln_components): + all_cves = [] + for wc in vuln_components: + for cve in wc["cve"]: + all_cves.append(cve["name"]) + return list(set(all_cves)) + + +def get_open_ports(asset): + services = asset["asset"]["services"] + open_ports = [] + for service in services: + open_ports.append(service["port"]) + return open_ports + + +def get_unnecessary_exposed_headers(asset): + headers = asset["asset"]["headers"] + exposed_headers = [] + for header in headers: + if header["headerName"] == "x-frame-options" and header["headerValue"] not in [ + "sameorigin", + "deny", + ]: + print(header["headerName"] + ":", header["headerValue"]) + exposed_headers.append(header["headerName"]) + elif header["headerName"] in UNNECESSARY_EXPOSED_HEADERS: + print(header["headerName"]) + exposed_headers.append(header["headerName"]) + return exposed_headers + + +def get_unnecessary_open_ports(asset): + open_ports = get_open_ports(asset) + unnecessary_open_ports = [] + for port in open_ports: + for service, ports in UNNECESSARY_OPEN_PORTS.items(): + if port in ports: + print(service + ":", port) + unnecessary_open_ports.append(port) + return { + "open_ports": open_ports, + "unnecessary_open_ports": unnecessary_open_ports, + } + + +def get_whois(asset): + return asset["asset"]["domainAsset"] + + +def get_web_component_types(asset): + web_components = get_web_components(asset) + types = [] + for wc in web_components: + types.append(wc["type"]) + return list(set(types)) + + +def three_one_two(asset): + types = get_web_component_types(asset) + if "DDOS Protection" in types: + return True + return False + + +def three_one_three(asset): + types = get_web_component_types(asset) + if "CDN" in types: + return True + return False diff --git a/azure-defender-easm/scripts/easm_update_asset.py b/azure-defender-easm/scripts/easm_update_asset.py new file mode 100644 index 000000000..4a0a6a090 --- /dev/null +++ b/azure-defender-easm/scripts/easm_update_asset.py @@ -0,0 +1,39 @@ +from easm_client import EASM_CLIENT + + +def update_hosts_with_org_slug_label(domains): + for domain in domains: + update_request = {"labels": [domain["orgSlug"]]} + asset_filter = f"kind = host AND name = {domain.domain}" + update = EASM_CLIENT.assets.update(body=update_request, filter=asset_filter) + print(f'{update["id"]}: {update["state"]}') + + +def update_hosts_with_external_id(domains): + for domain in domains: + update_request = {"externalId": [domain["id"]]} + asset_filter = "kind = host AND name = " + domain["domain"] + update = EASM_CLIENT.assets.update(body=update_request, filter=asset_filter) + print(f'{update["id"]}: {update["state"]}') + + +def label_assets(assets, label): + update_ids = [] + for asset in assets: + update_request = {"labels": {f"{label}": True}} + asset_filter = f"uuid = {asset['AssetUuid']}" + update = EASM_CLIENT.assets.update(body=update_request, filter=asset_filter) + update_ids.append(update["id"]) + print(f"{asset['AssetName']} labeled with {label}") + + # Using the `tasks` EASM_CLIENT, we can view the progress of each update using the `get` method + for update_id in update_ids: + update = EASM_CLIENT.tasks.get(update_id) + print(f'{update["id"]}: {update["state"]}') + + +def label_asset(asset, label): + update_request = {"labels": {f"{label}": True}} + asset_filter = f"uuid = {asset['AssetUuid']}" + update = EASM_CLIENT.assets.update(body=update_request, filter=asset_filter) + print(f'{update["id"]}: {update["state"]}') diff --git a/azure-defender-easm/scripts/easm_view_assets.py b/azure-defender-easm/scripts/easm_view_assets.py new file mode 100644 index 000000000..472492e20 --- /dev/null +++ b/azure-defender-easm/scripts/easm_view_assets.py @@ -0,0 +1,102 @@ +from easm_client import EASM_CLIENT +from easm_discover_assets import list_disco_group_runs + + +def get_login_pages(): + login_pages = [] + for asset in EASM_CLIENT.assets.list( + filter="kind = page AND (url ~ /login OR url ~ /log-in OR url ~ /signin OR url ~ /sign-in) AND url !~ api.canada.ca", + ): + # print asset if it has a login page + if ".gc.ca" in asset["name"] or ".canada.ca" in asset["name"]: + print(asset["name"]) + login_pages.append(asset) + + return login_pages + + +def get_register_pages(): + register_pages = [] + for asset in EASM_CLIENT.assets.list( + filter="kind = page AND (url ~/register OR url ~/signup OR url ~/sign-up) AND url !~ api.canada.ca", + ): + # print asset if it has a login page + if ".gc.ca" in asset["name"] or ".canada.ca" in asset["name"]: + print(asset["name"]) + register_pages.append(asset) + + return register_pages + + +def get_web_components(asset): + return asset["asset"]["webComponents"] + + +def get_cookies(asset): + return asset["asset"]["cookies"] + + +def enumerate_attributes_types(): + type_enums = set() + for asset in EASM_CLIENT.assets.list(filter="kind = page AND state = confirmed"): + length = len(type_enums) + try: + attributes = asset["asset"]["attributes"] + for attribute in attributes: + type_enums.add(attribute["attributeType"]) + except KeyError: + pass + + if len(type_enums) > length: + print(type_enums) + + return type_enums + + +def enumerate_wc_types(): + type_enums = set() + for asset in EASM_CLIENT.assets.list(filter="kind = host AND state = confirmed"): + length = len(type_enums) + try: + web_components = asset["asset"]["webComponents"] + for wc in web_components: + type_enums.add(wc["type"]) + except KeyError: + pass + + if len(type_enums) > length: + print(type_enums) + + return type_enums + + +def find_gc_hosts(): + assets = [] + for asset in EASM_CLIENT.assets.list( + filter="kind = host AND state = confirmed AND wildcard = false", + ): + if ".gc.ca" in asset["name"] or ".canada.ca" in asset["name"]: + print(asset["name"]) + assets.append(asset) + return assets + + +def find_disco_group_assets(group_name): + group_runs = list_disco_group_runs(group_name) + + # sort by most recent run + group_runs = sorted(group_runs, key=lambda k: k["submittedDate"], reverse=True) + latest_run = group_runs[0] + print(f"Latest run: {latest_run}") + + assets = [] + for asset in EASM_CLIENT.assets.list( + filter=f"kind = host AND state = confirmed AND wildcard = false", + ): + print(asset["name"]) + assets.append(asset) + return assets + + +if __name__ == "__main__": + find_disco_group_assets("Elections") diff --git a/azure-defender-easm/scripts/kusto_client.py b/azure-defender-easm/scripts/kusto_client.py new file mode 100644 index 000000000..b1abf1be1 --- /dev/null +++ b/azure-defender-easm/scripts/kusto_client.py @@ -0,0 +1,21 @@ +from azure.kusto.data import KustoClient, KustoConnectionStringBuilder + +import os +from dotenv import load_dotenv + +load_dotenv() + +KUSTO_CLUSTER = os.getenv("KUSTO_CLUSTER") +REGION = os.getenv("REGION") +KUSTO_DATABASE = os.getenv("KUSTO_DATABASE") +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +AUTHORITY_ID = os.getenv("TENANT_ID") + +KCSB_DATA = KustoConnectionStringBuilder.with_aad_application_key_authentication( + f"https://{KUSTO_CLUSTER}.{REGION}.kusto.windows.net", + CLIENT_ID, + CLIENT_SECRET, + AUTHORITY_ID, +) +KUSTO_CLIENT = KustoClient(KCSB_DATA) diff --git a/azure-defender-easm/scripts/kusto_queries.py b/azure-defender-easm/scripts/kusto_queries.py new file mode 100644 index 000000000..07ee98b2e --- /dev/null +++ b/azure-defender-easm/scripts/kusto_queries.py @@ -0,0 +1,120 @@ +from kusto_client import KUSTO_CLIENT, KUSTO_DATABASE +from azure.kusto.data.helpers import dataframe_from_result_table + + +def get_host_asset(host_name): + query = f""" + declare query_parameters(hostName:string = '{host_name}'); + EasmHostAsset + | where Host == hostName + | limit 1 + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + + +def get_page_asset(page_name): + query = f""" + declare query_parameters(pageName:string = '{page_name}'); + EasmPageAsset + | where Host == pageName + | take 1 + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + + +def get_host_assets(): + query = """ + EasmAsset + | where AssetName endswith '.gc.ca' or AssetName endswith '.canada.ca' + | where AssetType == 'HOST' + | summarize by AssetName, AssetUuid + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + + +def get_host_assets_by_labels(label): + query = f""" + declare query_parameters(label:string = '{label}'); + EasmAsset + | where AssetName endswith '.gc.ca' or AssetName endswith '.canada.ca' + | where AssetType == 'HOST' + | where Labels has label + | summarize by AssetName, AssetUuid, Labels + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + + +def get_hosts_with_ddos_protection(): + query = """ + EasmHostAsset + | where AssetName endswith '.gc.ca' or AssetName endswith '.canada.ca' + | where WebComponents has 'DDOS Protection' + | summarize by AssetName + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + + +def get_hosts_without_ddos_protection(): + query = """ + EasmHostAsset + | where AssetName endswith '.gc.ca' or AssetName endswith '.canada.ca' + | where WebComponents !has 'DDOS Protection' + | summarize by AssetName + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + + +def host_has_ddos_protection(domain): + query = f""" + declare query_parameters(domain:string = '{domain}'); + EasmHostAsset + | where AssetName == domain + | where WebComponents has 'DDOS Protection' + | project AssetName + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return len(data) > 0 + + +def get_unlabelled_org_assets_from_root(root): + query = f""" + declare query_parameters(domainRoot:string = '{root}'); + EasmAsset + | where TimeGeneratedValue > ago(24h) + | where AssetType == 'HOST' + | where AssetName == domainRoot or AssetName endswith '.' + domainRoot + | where Labels == '[]' + | project AssetName, AssetUuid, Labels + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data diff --git a/azure-defender-easm/scripts/replace_org_label_for_domain_root.py b/azure-defender-easm/scripts/replace_org_label_for_domain_root.py new file mode 100644 index 000000000..62a767884 --- /dev/null +++ b/azure-defender-easm/scripts/replace_org_label_for_domain_root.py @@ -0,0 +1,41 @@ +from easm_client import EASM_CLIENT +from kusto_client import KUSTO_CLIENT, KUSTO_DATABASE +from azure.kusto.data.helpers import dataframe_from_result_table + + +def get_labelled_assets_with_root(root, label): + query = f""" + declare query_parameters(root:string = '{root}', label:string = '{label}'); + EasmAsset + | where AssetName == root or AssetName endswith '.' + root + | where Labels has label + | summarize by AssetName, AssetUuid, Labels + """ + response = KUSTO_CLIENT.execute(KUSTO_DATABASE, query) + data = dataframe_from_result_table(response.primary_results[0]).to_dict( + orient="records" + ) + return data + + +def switch_org_label_for_root(root, old_label, new_label): + # Get assets with old label + assets = get_labelled_assets_with_root(root, old_label) + # Remove old label + print("Removing old label") + for asset in assets: + update_request = {"labels": {f"{old_label}": False}} + asset_filter = f"uuid = {asset['uuid']}" + update = EASM_CLIENT.assets.update(body=update_request, filter=asset_filter) + print(f'{update["id"]}: {update["state"]}') + # Add new label + print("Adding new label") + for asset in assets: + update_request = {"labels": {f"{new_label}": True}} + asset_filter = f"uuid = {asset['uuid']}" + update = EASM_CLIENT.assets.update(body=update_request, filter=asset_filter) + print(f'{update["id"]}: {update["state"]}') + + +if __name__ == "__main__": + switch_org_label_for_root() diff --git a/azure-defender-easm/scripts/requirements.txt b/azure-defender-easm/scripts/requirements.txt new file mode 100644 index 000000000..b8db2b9db --- /dev/null +++ b/azure-defender-easm/scripts/requirements.txt @@ -0,0 +1,6 @@ +azure-identity==1.16.1 +azure-defender-easm==1.0.0b1 +azure-kusto-data==4.3.1 +python-dotenv==1.0.0 +python-arango==7.3.4 +nats-py==2.6.0 \ No newline at end of file diff --git a/ci/Dockerfile b/ci/Dockerfile index 62a72ea6e..345577a5f 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:21.04 +FROM ubuntu:22.04 # Python, don't write bytecode! ENV PYTHONDONTWRITEBYTECODE 1 diff --git a/ci/README.md b/ci/README.md index 2ac2a0ea1..349d81424 100644 --- a/ci/README.md +++ b/ci/README.md @@ -6,11 +6,11 @@ You can see it in the cloudbuild.yaml files under the name "track-compliance/ci" To build a copy of this image: ```sh -docker build -t gcr.io/track-compliance/ci . +docker build -t northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/ci . ``` To push to the registry you will need to configure docker with proper credentials. ```sh gcloud auth configure-docker -docker push gcr.io/track-compliance/ci +docker push northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/ci ``` diff --git a/ci/cloudbuild.yaml b/ci/cloudbuild.yaml index e07da8bb7..93dd5a60d 100644 --- a/ci/cloudbuild.yaml +++ b/ci/cloudbuild.yaml @@ -1,29 +1,23 @@ steps: - - - name: 'gcr.io/cloud-builders/docker' - id: build-if-master - entrypoint: 'bash' + - name: "gcr.io/cloud-builders/docker" + id: build + entrypoint: "bash" dir: ci args: - - '-c' - - | - if [[ "$BRANCH_NAME" == "master" ]] - then - docker build -t gcr.io/track-compliance/ci . - else - exit 0 - fi + - "-c" + - | + docker build -t northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/ci . - - name: 'gcr.io/cloud-builders/docker' + - name: "gcr.io/cloud-builders/docker" id: push-if-master - entrypoint: 'bash' + entrypoint: "bash" dir: ci args: - - '-c' - - | - if [[ "$BRANCH_NAME" == "master" ]] - then - docker push gcr.io/track-compliance/ci - else - exit 0 - fi + - "-c" + - | + if [[ "$BRANCH_NAME" == "master" ]] + then + docker push northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/ci + else + exit 0 + fi diff --git a/clients/python/Pipfile b/clients/python/Pipfile index 50a025aa9..67673719f 100644 --- a/clients/python/Pipfile +++ b/clients/python/Pipfile @@ -5,10 +5,10 @@ name = "pypi" [packages] gql = ">=3.0.0a5" -aiohttp = "*" +aiohttp = "==3.11.7" websockets = "*" python-slugify = "*" -cryptography = "~=41.0.7" +cryptography = "~=44.0.1" [dev-packages] black = "*" diff --git a/clients/python/Pipfile.lock b/clients/python/Pipfile.lock index c4b149602..8cd2ab30d 100644 --- a/clients/python/Pipfile.lock +++ b/clients/python/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "8b0948939bf5f8888f759588a083aacb1abb83f9d4bffdd439b06df2e04e7021" + "sha256": "1b2c6695e94c627a471b8b11bfda20644c0c792b303c7d049a4496f33d2eabdf" }, "pipfile-spec": 6, "requires": {}, @@ -14,36 +14,128 @@ ] }, "default": { + "aiohappyeyeballs": { + "hashes": [ + "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586", + "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572" + ], + "markers": "python_version >= '3.8'", + "version": "==2.4.3" + }, "aiohttp": { "hashes": [ - "sha256:173267050501e1537293df06723bc5e719990889e2820ba3932969983892e960", - "sha256:438f1f1555c02c50894604d94944cff188fe138b46467b7fa99fdceb51ab5842", - "sha256:90bed250d1435aef33a1f8c439c5056d5d25a44fe6caf33fcafafed805bad4dc", - "sha256:93c3b14747413f38f094a60e98f55e73831f0c9a23ae7faa3dc97d8963e13021", - "sha256:a6e70a38d883185b1921d8122759661c39ade54949770394412a9e713fec6fa7", - "sha256:b5036133c1ba77ed5a70208d2a021a90b76fdf8bf523ae33dae46d4f4380d86f", - "sha256:c138451a82cdbf65cddf952941d5c7a1a2cac8ce3bc618dee8d889e5251ec7a5", - "sha256:c94770383e49f9cc5912b926364ad022a6c8a5dbf5498933ca3a5713c6daf738", - "sha256:ea26536ae06df6dac021303a0df72c79e55512070e6a304ba93ad468a3a754dc" + "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0", + "sha256:01a8aca4af3da85cea5c90141d23f4b0eee3cbecfd33b029a45a80f28c66c668", + "sha256:04b0cc74d5a882c9dacaeeccc1444f0233212b6f5be8bc90833feef1e1ce14b9", + "sha256:0de6466b9d742b4ee56fe1b2440706e225eb48c77c63152b1584864a236e7a50", + "sha256:12724f3a211fa243570e601f65a8831372caf1a149d2f1859f68479f07efec3d", + "sha256:12e4d45847a174f77b2b9919719203769f220058f642b08504cf8b1cf185dacf", + "sha256:17829f37c0d31d89aa6b8b010475a10233774771f9b6dc2cc352ea4f8ce95d9a", + "sha256:1a17f6a230f81eb53282503823f59d61dff14fb2a93847bf0399dc8e87817307", + "sha256:1cf03d27885f8c5ebf3993a220cc84fc66375e1e6e812731f51aab2b2748f4a6", + "sha256:1fbf41a6bbc319a7816ae0f0177c265b62f2a59ad301a0e49b395746eb2a9884", + "sha256:2257bdd5cf54a4039a4337162cd8048f05a724380a2283df34620f55d4e29341", + "sha256:24054fce8c6d6f33a3e35d1c603ef1b91bbcba73e3f04a22b4f2f27dac59b347", + "sha256:241a6ca732d2766836d62c58c49ca7a93d08251daef0c1e3c850df1d1ca0cbc4", + "sha256:28c7af3e50e5903d21d7b935aceed901cc2475463bc16ddd5587653548661fdb", + "sha256:351849aca2c6f814575c1a485c01c17a4240413f960df1bf9f5deb0003c61a53", + "sha256:3ce18f703b7298e7f7633efd6a90138d99a3f9a656cb52c1201e76cb5d79cf08", + "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8", + "sha256:3dd3e7e7c9ef3e7214f014f1ae260892286647b3cf7c7f1b644a568fd410f8ca", + "sha256:43bfd25113c1e98aec6c70e26d5f4331efbf4aa9037ba9ad88f090853bf64d7f", + "sha256:43dd89a6194f6ab02a3fe36b09e42e2df19c211fc2050ce37374d96f39604997", + "sha256:481f10a1a45c5f4c4a578bbd74cff22eb64460a6549819242a87a80788461fba", + "sha256:4ba8d043fed7ffa117024d7ba66fdea011c0e7602327c6d73cacaea38abe4491", + "sha256:4bb7493c3e3a36d3012b8564bd0e2783259ddd7ef3a81a74f0dbfa000fce48b7", + "sha256:4c1a6309005acc4b2bcc577ba3b9169fea52638709ffacbd071f3503264620da", + "sha256:4dda726f89bfa5c465ba45b76515135a3ece0088dfa2da49b8bb278f3bdeea12", + "sha256:53c921b58fdc6485d6b2603e0132bb01cd59b8f0620ffc0907f525e0ba071687", + "sha256:5578cf40440eafcb054cf859964bc120ab52ebe0e0562d2b898126d868749629", + "sha256:59ee1925b5a5efdf6c4e7be51deee93984d0ac14a6897bd521b498b9916f1544", + "sha256:670847ee6aeb3a569cd7cdfbe0c3bec1d44828bbfbe78c5d305f7f804870ef9e", + "sha256:78c657ece7a73b976905ab9ec8be9ef2df12ed8984c24598a1791c58ce3b4ce4", + "sha256:7a9318da4b4ada9a67c1dd84d1c0834123081e746bee311a16bb449f363d965e", + "sha256:7b2f8107a3c329789f3c00b2daad0e35f548d0a55cda6291579136622099a46e", + "sha256:7ea4490360b605804bea8173d2d086b6c379d6bb22ac434de605a9cbce006e7d", + "sha256:8360c7cc620abb320e1b8d603c39095101391a82b1d0be05fb2225471c9c5c52", + "sha256:875f7100ce0e74af51d4139495eec4025affa1a605280f23990b6434b81df1bd", + "sha256:8bedb1f6cb919af3b6353921c71281b1491f948ca64408871465d889b4ee1b66", + "sha256:8d20cfe63a1c135d26bde8c1d0ea46fd1200884afbc523466d2f1cf517d1fe33", + "sha256:9202f184cc0582b1db15056f2225ab4c1e3dac4d9ade50dd0613ac3c46352ac2", + "sha256:9acfc7f652b31853eed3b92095b0acf06fd5597eeea42e939bd23a17137679d5", + "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd", + "sha256:9e67531370a3b07e49b280c1f8c2df67985c790ad2834d1b288a2f13cd341c5f", + "sha256:9ee6a4cdcbf54b8083dc9723cdf5f41f722c00db40ccf9ec2616e27869151129", + "sha256:a7d9a606355655617fee25dd7e54d3af50804d002f1fd3118dd6312d26692d70", + "sha256:aa3705a8d14de39898da0fbad920b2a37b7547c3afd2a18b9b81f0223b7d0f68", + "sha256:b7215bf2b53bc6cb35808149980c2ae80a4ae4e273890ac85459c014d5aa60ac", + "sha256:badda65ac99555791eed75e234afb94686ed2317670c68bff8a4498acdaee935", + "sha256:bf0e6cce113596377cadda4e3ac5fb89f095bd492226e46d91b4baef1dd16f60", + "sha256:c171fc35d3174bbf4787381716564042a4cbc008824d8195eede3d9b938e29a8", + "sha256:c1f6490dd1862af5aae6cfcf2a274bffa9a5b32a8f5acb519a7ecf5a99a88866", + "sha256:c25b74a811dba37c7ea6a14d99eb9402d89c8d739d50748a75f3cf994cf19c43", + "sha256:c6095aaf852c34f42e1bd0cf0dc32d1e4b48a90bfb5054abdbb9d64b36acadcb", + "sha256:c63f898f683d1379b9be5afc3dd139e20b30b0b1e0bf69a3fc3681f364cf1629", + "sha256:cd8d62cab363dfe713067027a5adb4907515861f1e4ce63e7be810b83668b847", + "sha256:ce91a24aac80de6be8512fb1c4838a9881aa713f44f4e91dd7bb3b34061b497d", + "sha256:cea52d11e02123f125f9055dfe0ccf1c3857225fb879e4a944fae12989e2aef2", + "sha256:cf4efa2d01f697a7dbd0509891a286a4af0d86902fc594e20e3b1712c28c0106", + "sha256:d2fa6fc7cc865d26ff42480ac9b52b8c9b7da30a10a6442a9cdf429de840e949", + "sha256:d329300fb23e14ed1f8c6d688dfd867d1dcc3b1d7cd49b7f8c5b44e797ce0932", + "sha256:d6177077a31b1aecfc3c9070bd2f11419dbb4a70f30f4c65b124714f525c2e48", + "sha256:db37248535d1ae40735d15bdf26ad43be19e3d93ab3f3dad8507eb0f85bb8124", + "sha256:db70a47987e34494b451a334605bee57a126fe8d290511349e86810b4be53b01", + "sha256:dcefcf2915a2dbdbce37e2fc1622129a1918abfe3d06721ce9f6cdac9b6d2eaa", + "sha256:dda3ed0a7869d2fa16aa41f9961ade73aa2c2e3b2fcb0a352524e7b744881889", + "sha256:e0bf378db07df0a713a1e32381a1b277e62ad106d0dbe17b5479e76ec706d720", + "sha256:e13a05db87d3b241c186d0936808d0e4e12decc267c617d54e9c643807e968b6", + "sha256:e143b0ef9cb1a2b4f74f56d4fbe50caa7c2bb93390aff52f9398d21d89bc73ea", + "sha256:e22d1721c978a6494adc824e0916f9d187fa57baeda34b55140315fa2f740184", + "sha256:e5522ee72f95661e79db691310290c4618b86dff2d9b90baedf343fd7a08bf79", + "sha256:e993676c71288618eb07e20622572b1250d8713e7e00ab3aabae28cb70f3640d", + "sha256:ee9afa1b0d2293c46954f47f33e150798ad68b78925e3710044e0d67a9487791", + "sha256:f1ac5462582d6561c1c1708853a9faf612ff4e5ea5e679e99be36143d6eabd8e", + "sha256:f5022504adab881e2d801a88b748ea63f2a9d130e0b2c430824682a96f6534be", + "sha256:f5b973cce96793725ef63eb449adfb74f99c043c718acb76e0d2a447ae369962", + "sha256:f7c58a240260822dc07f6ae32a0293dd5bccd618bb2d0f36d51c5dbd526f89c0", + "sha256:fc6da202068e0a268e298d7cd09b6e9f3997736cd9b060e2750963754552a0a9", + "sha256:fdadc3f6a32d6eca45f9a900a254757fd7855dfb2d8f8dcf0e88f0fae3ff8eb1" ], "index": "pypi", - "version": "==4.0.0a1" + "markers": "python_version >= '3.9'", + "version": "==3.11.7" + }, + "aiosignal": { + "hashes": [ + "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", + "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" + }, + "anyio": { + "hashes": [ + "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", + "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6" + ], + "markers": "python_version >= '3.8'", + "version": "==4.3.0" }, "async-timeout": { "hashes": [ - "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", - "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" + "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", + "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.0.1" + "markers": "python_version < '3.11'", + "version": "==4.0.3" }, "attrs": { "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.1.0" + "version": "==24.2.0" }, "backoff": { "hashes": [ @@ -55,161 +147,471 @@ }, "cffi": { "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "markers": "python_version >= '3.8'", - "version": "==1.16.0" + "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", + "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", + "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", + "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", + "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", + "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", + "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", + "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", + "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", + "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", + "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", + "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", + "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", + "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", + "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", + "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", + "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", + "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", + "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", + "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b", + "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", + "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", + "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", + "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", + "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", + "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", + "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8", + "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1", + "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", + "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", + "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", + "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", + "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", + "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", + "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", + "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", + "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", + "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", + "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", + "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", + "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", + "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", + "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", + "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", + "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", + "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", + "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", + "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", + "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", + "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", + "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", + "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", + "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", + "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", + "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", + "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", + "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", + "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", + "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", + "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", + "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", + "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", + "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", + "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", + "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", + "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", + "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.17.1" }, - "chardet": { + "cryptography": { "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + "sha256:00918d859aa4e57db8299607086f793fa7813ae2ff5a4637e318a25ef82730f7", + "sha256:1e8d181e90a777b63f3f0caa836844a1182f1f265687fac2115fcf245f5fbec3", + "sha256:1f9a92144fa0c877117e9748c74501bea842f93d21ee00b0cf922846d9d0b183", + "sha256:21377472ca4ada2906bc313168c9dc7b1d7ca417b63c1c3011d0c74b7de9ae69", + "sha256:24979e9f2040c953a94bf3c6782e67795a4c260734e5264dceea65c8f4bae64a", + "sha256:2a46a89ad3e6176223b632056f321bc7de36b9f9b93b2cc1cccf935a3849dc62", + "sha256:322eb03ecc62784536bc173f1483e76747aafeb69c8728df48537eb431cd1911", + "sha256:436df4f203482f41aad60ed1813811ac4ab102765ecae7a2bbb1dbb66dcff5a7", + "sha256:4f422e8c6a28cf8b7f883eb790695d6d45b0c385a2583073f3cec434cc705e1a", + "sha256:53f23339864b617a3dfc2b0ac8d5c432625c80014c25caac9082314e9de56f41", + "sha256:5fed5cd6102bb4eb843e3315d2bf25fede494509bddadb81e03a859c1bc17b83", + "sha256:610a83540765a8d8ce0f351ce42e26e53e1f774a6efb71eb1b41eb01d01c3d12", + "sha256:6c8acf6f3d1f47acb2248ec3ea261171a671f3d9428e34ad0357148d492c7864", + "sha256:6f76fdd6fd048576a04c5210d53aa04ca34d2ed63336d4abd306d0cbe298fddf", + "sha256:72198e2b5925155497a5a3e8c216c7fb3e64c16ccee11f0e7da272fa93b35c4c", + "sha256:887143b9ff6bad2b7570da75a7fe8bbf5f65276365ac259a5d2d5147a73775f2", + "sha256:888fcc3fce0c888785a4876ca55f9f43787f4c5c1cc1e2e0da71ad481ff82c5b", + "sha256:8e6a85a93d0642bd774460a86513c5d9d80b5c002ca9693e63f6e540f1815ed0", + "sha256:94f99f2b943b354a5b6307d7e8d19f5c423a794462bde2bf310c770ba052b1c4", + "sha256:9b336599e2cb77b1008cb2ac264b290803ec5e8e89d618a5e978ff5eb6f715d9", + "sha256:a2d8a7045e1ab9b9f803f0d9531ead85f90c5f2859e653b61497228b18452008", + "sha256:b8272f257cf1cbd3f2e120f14c68bff2b6bdfcc157fafdee84a1b795efd72862", + "sha256:bf688f615c29bfe9dfc44312ca470989279f0e94bb9f631f85e3459af8efc009", + "sha256:d9c5b9f698a83c8bd71e0f4d3f9f839ef244798e5ffe96febfa9714717db7af7", + "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f", + "sha256:df978682c1504fc93b3209de21aeabf2375cb1571d4e61907b3e7a2540e83026", + "sha256:e403f7f766ded778ecdb790da786b418a9f2394f36e8cc8b796cc056ab05f44f", + "sha256:eb3889330f2a4a148abead555399ec9a32b13b7c8ba969b72d8e500eb7ef84cd", + "sha256:f4daefc971c2d1f82f03097dc6f216744a6cd2ac0f04c68fb935ea2ba2a0d420", + "sha256:f51f5705ab27898afda1aaa430f34ad90dc117421057782022edf0600bec5f14", + "sha256:fd0ee90072861e276b0ff08bd627abec29e32a53b2be44e41dbcdf87cbee2b00" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==3.0.4" + "index": "pypi", + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.1" }, - "cryptography": { + "exceptiongroup": { "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", + "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==41.0.7" + "markers": "python_version < '3.11'", + "version": "==1.2.1" + }, + "frozenlist": { + "hashes": [ + "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", + "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf", + "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6", + "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a", + "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d", + "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f", + "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", + "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", + "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", + "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", + "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec", + "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2", + "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c", + "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336", + "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4", + "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d", + "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b", + "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c", + "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10", + "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08", + "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942", + "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", + "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f", + "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10", + "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5", + "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", + "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", + "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", + "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d", + "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923", + "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", + "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", + "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17", + "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0", + "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", + "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", + "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c", + "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a", + "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0", + "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", + "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab", + "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", + "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3", + "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", + "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", + "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604", + "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", + "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5", + "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", + "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", + "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", + "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", + "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d", + "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", + "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3", + "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", + "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", + "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9", + "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf", + "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76", + "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba", + "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171", + "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb", + "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", + "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", + "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972", + "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d", + "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869", + "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9", + "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411", + "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723", + "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2", + "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b", + "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99", + "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e", + "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", + "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3", + "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb", + "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", + "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", + "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca", + "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45", + "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", + "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f", + "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5", + "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307", + "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e", + "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2", + "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778", + "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a", + "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30", + "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.0" }, "gql": { "hashes": [ - "sha256:5f2e5d55d3b30d4d94fcd236e9766e00f4db1e94987de5321cb4c075741620fb", - "sha256:6b853c42d1018913281cc07557314fcd2a362f2da306e831033bda2fa72d191a" + "sha256:c681d2273cc8164c0d6557c2d6fd4df190a009706a2044cd640be6a24526318e", + "sha256:f1a4fc06186f25e5b4b5abaf3af359bc7ac65b38bcaa705b6507cd29dec2ecbf" ], "index": "pypi", - "version": "==3.5.0b4" + "version": "==3.6.0b2" }, "graphql-core": { "hashes": [ - "sha256:065d23881d00d0b52b9a7c0a8d63451585549c13f3f05a214923b20dabbda290", - "sha256:b441346e61b2d7465a6f4001334ba1ddb123498a7e9dfe76cd323db8159aa707" + "sha256:9d72ed2c4ac93682fe55a0a2939548c1b3d23bd7e2ad76f7f8faef4d99d606ec", + "sha256:f3a8ab44a436651608b8e0335800b175c4bb3a7bf0217bbc97babf6d9517218a" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==3.3.0a3" + "version": "==3.3.0a5" }, "idna": { "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], - "markers": "python_version >= '3.5'", - "version": "==3.4" + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "multidict": { "hashes": [ - "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", - "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", - "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", - "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", - "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", - "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", - "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", - "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", - "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", - "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", - "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", - "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", - "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", - "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", - "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", - "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", - "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" + "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", + "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", + "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", + "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", + "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", + "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", + "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", + "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", + "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f", + "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", + "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6", + "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada", + "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", + "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2", + "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", + "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", + "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef", + "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", + "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", + "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", + "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6", + "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", + "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478", + "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", + "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", + "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", + "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", + "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", + "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30", + "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", + "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", + "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", + "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", + "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c", + "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", + "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", + "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", + "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", + "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", + "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2", + "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", + "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", + "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", + "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", + "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657", + "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", + "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492", + "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43", + "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", + "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", + "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", + "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057", + "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc", + "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", + "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255", + "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", + "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972", + "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", + "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1", + "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", + "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a", + "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", + "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c", + "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd", + "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", + "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", + "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", + "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", + "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", + "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", + "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4", + "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", + "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", + "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28", + "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d", + "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a", + "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", + "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", + "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", + "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", + "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", + "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", + "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392", + "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167", + "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c", + "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", + "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", + "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", + "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875", + "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd", + "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", + "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db" ], - "markers": "python_version >= '3.5'", - "version": "==4.7.6" + "markers": "python_version >= '3.8'", + "version": "==6.1.0" + }, + "propcache": { + "hashes": [ + "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9", + "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763", + "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325", + "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb", + "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b", + "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09", + "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957", + "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68", + "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f", + "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798", + "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418", + "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6", + "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162", + "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f", + "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036", + "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8", + "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2", + "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110", + "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23", + "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8", + "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638", + "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a", + "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44", + "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2", + "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2", + "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850", + "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136", + "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b", + "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887", + "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89", + "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87", + "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348", + "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4", + "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861", + "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e", + "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c", + "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b", + "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb", + "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1", + "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de", + "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354", + "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563", + "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5", + "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf", + "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9", + "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12", + "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4", + "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5", + "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71", + "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9", + "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed", + "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336", + "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90", + "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063", + "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad", + "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6", + "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8", + "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e", + "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2", + "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7", + "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d", + "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d", + "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df", + "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b", + "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178", + "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2", + "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630", + "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48", + "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61", + "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89", + "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb", + "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3", + "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6", + "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562", + "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b", + "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58", + "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db", + "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99", + "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37", + "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83", + "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a", + "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d", + "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04", + "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70", + "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544", + "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394", + "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea", + "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7", + "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1", + "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793", + "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577", + "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7", + "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57", + "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d", + "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032", + "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d", + "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016", + "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504" + ], + "markers": "python_version >= '3.8'", + "version": "==0.2.0" }, "pycparser": { "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" ], - "version": "==2.21" + "markers": "python_version >= '3.8'", + "version": "==2.22" }, "python-slugify": { "hashes": [ - "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395", - "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27" + "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", + "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856" ], "index": "pypi", - "version": "==8.0.1" + "markers": "python_version >= '3.7'", + "version": "==8.0.4" + }, + "sniffio": { + "hashes": [ + "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" }, "text-unidecode": { "hashes": [ @@ -220,479 +622,493 @@ }, "typing-extensions": { "hashes": [ - "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", - "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2" + "sha256:be199d06d8f09ca2c9425e3aa04a9afba33e892fe079dea959e72df7f8442343", + "sha256:f933a7b288a919ca97adbff656e52ff81f7ff25d98a2aabb9355ca4090f772fe" ], - "markers": "python_version >= '3.7'", - "version": "==4.7.1" + "markers": "python_version < '3.11'", + "version": "==4.12.0rc1" }, "websockets": { "hashes": [ - "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd", - "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f", - "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998", - "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82", - "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788", - "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa", - "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f", - "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4", - "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7", - "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f", - "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd", - "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69", - "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb", - "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b", - "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016", - "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac", - "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4", - "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb", - "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99", - "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e", - "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54", - "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf", - "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007", - "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3", - "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6", - "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86", - "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1", - "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61", - "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11", - "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8", - "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f", - "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931", - "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526", - "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016", - "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae", - "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd", - "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b", - "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311", - "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af", - "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152", - "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288", - "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de", - "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97", - "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d", - "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d", - "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca", - "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0", - "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9", - "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b", - "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e", - "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128", - "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d", - "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c", - "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5", - "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6", - "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b", - "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b", - "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280", - "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c", - "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c", - "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f", - "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20", - "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8", - "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb", - "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602", - "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf", - "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0", - "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74", - "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0", - "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564" + "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b", + "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6", + "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", + "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", + "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205", + "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892", + "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53", + "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", + "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", + "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c", + "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", + "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", + "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931", + "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", + "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370", + "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", + "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec", + "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", + "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62", + "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", + "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", + "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", + "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123", + "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9", + "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", + "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", + "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", + "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", + "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438", + "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137", + "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", + "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", + "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", + "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", + "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", + "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", + "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967", + "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", + "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d", + "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def", + "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", + "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", + "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2", + "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", + "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b", + "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28", + "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7", + "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d", + "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", + "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468", + "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8", + "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae", + "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611", + "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", + "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9", + "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", + "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", + "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2", + "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", + "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", + "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6", + "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", + "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", + "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", + "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", + "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399", + "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", + "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", + "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", + "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", + "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8", + "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7" ], "index": "pypi", - "version": "==11.0.3" + "markers": "python_version >= '3.8'", + "version": "==12.0" }, "yarl": { "hashes": [ - "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571", - "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3", - "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3", - "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c", - "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7", - "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04", - "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191", - "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea", - "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4", - "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4", - "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095", - "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e", - "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74", - "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef", - "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33", - "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde", - "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45", - "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf", - "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b", - "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac", - "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0", - "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528", - "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716", - "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb", - "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18", - "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72", - "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6", - "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582", - "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5", - "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368", - "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc", - "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9", - "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be", - "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a", - "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80", - "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8", - "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6", - "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417", - "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574", - "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59", - "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608", - "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82", - "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1", - "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3", - "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d", - "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8", - "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc", - "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac", - "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8", - "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955", - "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0", - "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367", - "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb", - "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a", - "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623", - "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2", - "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6", - "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7", - "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4", - "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051", - "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938", - "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8", - "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9", - "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3", - "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5", - "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9", - "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333", - "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185", - "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3", - "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560", - "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b", - "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7", - "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78", - "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.2" + "sha256:01be8688fc211dc237e628fcc209dda412d35de7642453059a0553747018d075", + "sha256:039c299a0864d1f43c3e31570045635034ea7021db41bf4842693a72aca8df3a", + "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7", + "sha256:13aaf2bdbc8c86ddce48626b15f4987f22e80d898818d735b20bd58f17292ee8", + "sha256:14408cc4d34e202caba7b5ac9cc84700e3421a9e2d1b157d744d101b061a4a88", + "sha256:1db1537e9cb846eb0ff206eac667f627794be8b71368c1ab3207ec7b6f8c5afc", + "sha256:1ece25e2251c28bab737bdf0519c88189b3dd9492dc086a1d77336d940c28ced", + "sha256:1ff116f0285b5c8b3b9a2680aeca29a858b3b9e0402fc79fd850b32c2bcb9f8b", + "sha256:205de377bd23365cd85562c9c6c33844050a93661640fda38e0567d2826b50df", + "sha256:20d95535e7d833889982bfe7cc321b7f63bf8879788fee982c76ae2b24cfb715", + "sha256:20de4a8b04de70c49698dc2390b7fd2d18d424d3b876371f9b775e2b462d4b41", + "sha256:2d90f2e4d16a5b0915ee065218b435d2ef619dd228973b1b47d262a6f7cd8fa5", + "sha256:2e6b4466714a73f5251d84b471475850954f1fa6acce4d3f404da1d55d644c34", + "sha256:309f8d27d6f93ceeeb80aa6980e883aa57895270f7f41842b92247e65d7aeddf", + "sha256:32141e13a1d5a48525e519c9197d3f4d9744d818d5c7d6547524cc9eccc8971e", + "sha256:34176bfb082add67cb2a20abd85854165540891147f88b687a5ed0dc225750a0", + "sha256:38b39b7b3e692b6c92b986b00137a3891eddb66311b229d1940dcbd4f025083c", + "sha256:3a3709450a574d61be6ac53d582496014342ea34876af8dc17cc16da32826c9a", + "sha256:3adaaf9c6b1b4fc258584f4443f24d775a2086aee82d1387e48a8b4f3d6aecf6", + "sha256:3f576ed278860df2721a5d57da3381040176ef1d07def9688a385c8330db61a1", + "sha256:42ba84e2ac26a3f252715f8ec17e6fdc0cbf95b9617c5367579fafcd7fba50eb", + "sha256:454902dc1830d935c90b5b53c863ba2a98dcde0fbaa31ca2ed1ad33b2a7171c6", + "sha256:466d31fd043ef9af822ee3f1df8fdff4e8c199a7f4012c2642006af240eade17", + "sha256:49a98ecadc5a241c9ba06de08127ee4796e1009555efd791bac514207862b43d", + "sha256:4d26f1fa9fa2167bb238f6f4b20218eb4e88dd3ef21bb8f97439fa6b5313e30d", + "sha256:52c136f348605974c9b1c878addd6b7a60e3bf2245833e370862009b86fa4689", + "sha256:536a7a8a53b75b2e98ff96edb2dfb91a26b81c4fed82782035767db5a465be46", + "sha256:576d258b21c1db4c6449b1c572c75d03f16a482eb380be8003682bdbe7db2f28", + "sha256:609ffd44fed2ed88d9b4ef62ee860cf86446cf066333ad4ce4123505b819e581", + "sha256:67b336c15e564d76869c9a21316f90edf546809a5796a083b8f57c845056bc01", + "sha256:685cc37f3f307c6a8e879986c6d85328f4c637f002e219f50e2ef66f7e062c1d", + "sha256:6a49ad0102c0f0ba839628d0bf45973c86ce7b590cdedf7540d5b1833ddc6f00", + "sha256:6fb64dd45453225f57d82c4764818d7a205ee31ce193e9f0086e493916bd4f72", + "sha256:701bb4a8f4de191c8c0cc9a1e6d5142f4df880e9d1210e333b829ca9425570ed", + "sha256:73553bbeea7d6ec88c08ad8027f4e992798f0abc459361bf06641c71972794dc", + "sha256:7520e799b1f84e095cce919bd6c23c9d49472deeef25fe1ef960b04cca51c3fc", + "sha256:7609b8462351c4836b3edce4201acb6dd46187b207c589b30a87ffd1813b48dc", + "sha256:7db9584235895a1dffca17e1c634b13870852094f6389b68dcc6338086aa7b08", + "sha256:7fa7d37f2ada0f42e0723632993ed422f2a679af0e200874d9d861720a54f53e", + "sha256:80741ec5b471fbdfb997821b2842c59660a1c930ceb42f8a84ba8ca0f25a66aa", + "sha256:8254dbfce84ee5d1e81051ee7a0f1536c108ba294c0fdb5933476398df0654f3", + "sha256:8b8d3e4e014fb4274f1c5bf61511d2199e263909fb0b8bda2a7428b0894e8dc6", + "sha256:8e1c18890091aa3cc8a77967943476b729dc2016f4cfe11e45d89b12519d4a93", + "sha256:9106025c7f261f9f5144f9aa7681d43867eed06349a7cfb297a1bc804de2f0d1", + "sha256:91b8fb9427e33f83ca2ba9501221ffaac1ecf0407f758c4d2f283c523da185ee", + "sha256:96404e8d5e1bbe36bdaa84ef89dc36f0e75939e060ca5cd45451aba01db02902", + "sha256:9b4c90c5363c6b0a54188122b61edb919c2cd1119684999d08cd5e538813a28e", + "sha256:a0509475d714df8f6d498935b3f307cd122c4ca76f7d426c7e1bb791bcd87eda", + "sha256:a173401d7821a2a81c7b47d4e7d5c4021375a1441af0c58611c1957445055056", + "sha256:a45d94075ac0647621eaaf693c8751813a3eccac455d423f473ffed38c8ac5c9", + "sha256:a5f72421246c21af6a92fbc8c13b6d4c5427dfd949049b937c3b731f2f9076bd", + "sha256:a64619a9c47c25582190af38e9eb382279ad42e1f06034f14d794670796016c0", + "sha256:a7ee6884a8848792d58b854946b685521f41d8871afa65e0d4a774954e9c9e89", + "sha256:ae38bd86eae3ba3d2ce5636cc9e23c80c9db2e9cb557e40b98153ed102b5a736", + "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a", + "sha256:b0a2074a37285570d54b55820687de3d2f2b9ecf1b714e482e48c9e7c0402038", + "sha256:b1a3297b9cad594e1ff0c040d2881d7d3a74124a3c73e00c3c71526a1234a9f7", + "sha256:b212452b80cae26cb767aa045b051740e464c5129b7bd739c58fbb7deb339e7b", + "sha256:b234a4a9248a9f000b7a5dfe84b8cb6210ee5120ae70eb72a4dcbdb4c528f72f", + "sha256:b4095c5019bb889aa866bf12ed4c85c0daea5aafcb7c20d1519f02a1e738f07f", + "sha256:b8e8c516dc4e1a51d86ac975b0350735007e554c962281c432eaa5822aa9765c", + "sha256:bd80ed29761490c622edde5dd70537ca8c992c2952eb62ed46984f8eff66d6e8", + "sha256:c083f6dd6951b86e484ebfc9c3524b49bcaa9c420cb4b2a78ef9f7a512bfcc85", + "sha256:c0f4808644baf0a434a3442df5e0bedf8d05208f0719cedcd499e168b23bfdc4", + "sha256:c4cb992d8090d5ae5f7afa6754d7211c578be0c45f54d3d94f7781c495d56716", + "sha256:c60e547c0a375c4bfcdd60eef82e7e0e8698bf84c239d715f5c1278a73050393", + "sha256:c73a6bbc97ba1b5a0c3c992ae93d721c395bdbb120492759b94cc1ac71bc6350", + "sha256:c893f8c1a6d48b25961e00922724732d00b39de8bb0b451307482dc87bddcd74", + "sha256:cd6ab7d6776c186f544f893b45ee0c883542b35e8a493db74665d2e594d3ca75", + "sha256:d89ae7de94631b60d468412c18290d358a9d805182373d804ec839978b120422", + "sha256:d9d4f5e471e8dc49b593a80766c2328257e405f943c56a3dc985c125732bc4cf", + "sha256:da206d1ec78438a563c5429ab808a2b23ad7bc025c8adbf08540dde202be37d5", + "sha256:dbf53db46f7cf176ee01d8d98c39381440776fcda13779d269a8ba664f69bec0", + "sha256:dd21c0128e301851de51bc607b0a6da50e82dc34e9601f4b508d08cc89ee7929", + "sha256:e2580c1d7e66e6d29d6e11855e3b1c6381971e0edd9a5066e6c14d79bc8967af", + "sha256:e3818eabaefb90adeb5e0f62f047310079d426387991106d4fbf3519eec7d90a", + "sha256:ed69af4fe2a0949b1ea1d012bf065c77b4c7822bad4737f17807af2adb15a73c", + "sha256:f172b8b2c72a13a06ea49225a9c47079549036ad1b34afa12d5491b881f5b993", + "sha256:f275ede6199d0f1ed4ea5d55a7b7573ccd40d97aee7808559e1298fe6efc8dbd", + "sha256:f7edeb1dcc7f50a2c8e08b9dc13a413903b7817e72273f00878cb70e766bdb3b", + "sha256:fa2c9cb607e0f660d48c54a63de7a9b36fef62f6b8bd50ff592ce1137e73ac7d", + "sha256:fe94d1de77c4cd8caff1bd5480e22342dbd54c93929f5943495d9c1e8abe9f42" + ], + "markers": "python_version >= '3.9'", + "version": "==1.18.0" } }, "develop": { "alabaster": { "hashes": [ - "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3", - "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2" + "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", + "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92" ], - "markers": "python_version >= '3.6'", - "version": "==0.7.13" + "markers": "python_version >= '3.9'", + "version": "==0.7.16" }, "astroid": { "hashes": [ - "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c", - "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd" + "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c", + "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b" ], - "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.6" + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.8" }, "babel": { "hashes": [ - "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610", - "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455" + "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb", + "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413" ], - "markers": "python_version >= '3.7'", - "version": "==2.12.1" + "markers": "python_version >= '3.8'", + "version": "==2.15.0" }, - "bandit": { + "backports.tarfile": { "hashes": [ - "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549", - "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e" + "sha256:73e0179647803d3726d82e76089d01d8549ceca9bace469953fcb4d97cf2d417", + "sha256:9c2ef9696cb73374f7164e17fc761389393ca76777036f5aad42e8b93fcd8009" ], - "index": "pypi", - "version": "==1.7.5" + "markers": "python_version < '3.12'", + "version": "==1.1.1" }, - "black": { + "bandit": { "hashes": [ - "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", - "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", - "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", - "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", - "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", - "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", - "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", - "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", - "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", - "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", - "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", - "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", - "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", - "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", - "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", - "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", - "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", - "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", - "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", - "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", - "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", - "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", - "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", - "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", - "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3" + "sha256:df6146ad73dd30e8cbda4e29689ddda48364e36ff655dbfc86998401fcf1721f", + "sha256:e00ad5a6bc676c0954669fe13818024d66b70e42cf5adb971480cf3b671e835f" ], "index": "pypi", - "version": "==23.3.0" + "markers": "python_version >= '3.9'", + "version": "==1.8.2" }, - "bleach": { + "black": { "hashes": [ - "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414", - "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4" + "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474", + "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1", + "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0", + "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8", + "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96", + "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1", + "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04", + "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021", + "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94", + "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d", + "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c", + "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7", + "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c", + "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc", + "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7", + "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d", + "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c", + "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741", + "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce", + "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb", + "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063", + "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e" ], - "markers": "python_version >= '3.7'", - "version": "==6.0.0" + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==24.4.2" }, "certifi": { "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" + "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", + "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3" ], "markers": "python_version >= '3.6'", - "version": "==2023.5.7" + "version": "==2025.4.26" }, "cffi": { "hashes": [ - "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", - "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", - "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", - "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", - "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", - "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", - "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", - "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", - "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", - "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", - "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", - "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", - "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", - "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", - "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", - "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", - "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", - "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", - "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", - "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", - "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", - "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", - "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", - "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", - "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", - "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", - "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", - "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", - "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", - "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", - "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", - "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", - "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", - "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", - "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", - "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", - "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", - "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", - "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", - "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", - "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", - "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", - "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", - "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", - "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", - "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", - "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", - "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", - "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", - "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", - "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", - "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", - "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", - "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", - "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", - "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", - "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", - "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", - "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", - "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", - "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", - "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", - "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", - "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" - ], - "version": "==1.15.1" + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.16.0" }, "charset-normalizer": { "hashes": [ - "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", - "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", - "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", - "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", - "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", - "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", - "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", - "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", - "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", - "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", - "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", - "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", - "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", - "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", - "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", - "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", - "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", - "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", - "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", - "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", - "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", - "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", - "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", - "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", - "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", - "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", - "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", - "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", - "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", - "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", - "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", - "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", - "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", - "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", - "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", - "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", - "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", - "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", - "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", - "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", - "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", - "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", - "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", - "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", - "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", - "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", - "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", - "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", - "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", - "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", - "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", - "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", - "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", - "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", - "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", - "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", - "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", - "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", - "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", - "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", - "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", - "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", - "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", - "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", - "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", - "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", - "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", - "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", - "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", - "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", - "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", - "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", - "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", - "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", - "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.2.0" + "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", + "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45", + "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", + "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", + "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", + "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", + "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d", + "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", + "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184", + "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", + "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b", + "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64", + "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", + "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", + "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", + "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344", + "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58", + "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", + "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", + "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", + "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", + "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", + "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", + "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", + "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", + "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1", + "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01", + "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", + "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58", + "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", + "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", + "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2", + "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a", + "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", + "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", + "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5", + "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb", + "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f", + "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", + "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", + "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", + "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", + "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7", + "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", + "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455", + "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", + "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4", + "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", + "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", + "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", + "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", + "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", + "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", + "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", + "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", + "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", + "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", + "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa", + "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", + "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", + "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", + "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", + "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", + "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", + "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02", + "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", + "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", + "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", + "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", + "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", + "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", + "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", + "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681", + "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", + "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", + "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a", + "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", + "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", + "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", + "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", + "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027", + "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", + "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", + "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", + "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", + "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", + "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", + "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da", + "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", + "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f", + "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", + "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.2" }, "click": { "hashes": [ - "sha256:2739815aaa5d2c986a88f1e9230c55e17f0caad3d958a5e13ad0797c166db9e3", - "sha256:b97d0c74955da062a7d4ef92fadb583806a585b2ea81958a81bd72726cbb8e37" + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" ], "markers": "python_version >= '3.7'", - "version": "==8.1.4" + "version": "==8.1.7" }, "cryptography": { "hashes": [ - "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db", - "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a", - "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039", - "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c", - "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3", - "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485", - "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c", - "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca", - "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5", - "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5", - "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3", - "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb", - "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43", - "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31", - "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc", - "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b", - "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006", - "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a", - "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699" + "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55", + "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785", + "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b", + "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886", + "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82", + "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1", + "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda", + "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f", + "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68", + "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60", + "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7", + "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd", + "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582", + "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc", + "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858", + "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b", + "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2", + "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678", + "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13", + "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4", + "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8", + "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604", + "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477", + "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e", + "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a", + "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9", + "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14", + "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda", + "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da", + "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562", + "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2", + "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9" ], "index": "pypi", - "version": "==41.0.1" + "markers": "python_version >= '3.7'", + "version": "==42.0.7" }, "dill": { "hashes": [ - "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0", - "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373" + "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", + "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c" ], - "markers": "python_version < '3.11'", - "version": "==0.3.6" + "markers": "python_version >= '3.11'", + "version": "==0.3.9" }, "docutils": { "hashes": [ - "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6", - "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b" + "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", + "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2" ], - "markers": "python_version >= '3.7'", - "version": "==0.20.1" + "markers": "python_version >= '3.9'", + "version": "==0.21.2" }, "exceptiongroup": { "hashes": [ - "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5", - "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f" + "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", + "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" ], "markers": "python_version < '3.11'", - "version": "==1.1.2" - }, - "gitdb": { - "hashes": [ - "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a", - "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7" - ], - "markers": "python_version >= '3.7'", - "version": "==4.0.10" - }, - "gitpython": { - "hashes": [ - "sha256:8d9b8cb1e80b9735e8717c9362079d3ce4c6e5ddeebedd0361b228c3a67a62f6", - "sha256:e3d59b1c2c6ebb9dfa7a184daf3b6dd4914237e7488a1730a6d8f6f5d0b4187f" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.32" + "version": "==1.2.1" }, "idna": { "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], - "markers": "python_version >= '3.5'", - "version": "==3.4" + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "imagesize": { "hashes": [ @@ -704,11 +1120,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb", - "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743" + "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", + "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2" ], - "markers": "python_version < '3.10'", - "version": "==6.8.0" + "markers": "python_version >= '3.8'", + "version": "==7.1.0" }, "iniconfig": { "hashes": [ @@ -720,19 +1136,35 @@ }, "isort": { "hashes": [ - "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", - "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" + "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892", + "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1" ], - "markers": "python_full_version >= '3.8.0'", - "version": "==5.12.0" + "markers": "python_full_version >= '3.9.0'", + "version": "==6.0.0" }, "jaraco.classes": { "hashes": [ - "sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb", - "sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621" + "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", + "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" ], "markers": "python_version >= '3.8'", - "version": "==3.3.0" + "version": "==3.4.0" + }, + "jaraco.context": { + "hashes": [ + "sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266", + "sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2" + ], + "markers": "python_version >= '3.8'", + "version": "==5.3.0" + }, + "jaraco.functools": { + "hashes": [ + "sha256:3b24ccb921d6b593bdceb56ce14799204f473976e2a9d4b15b04d0f2c2326664", + "sha256:d33fa765374c0611b52f8b3a795f8900869aa88c84769d4d1746cd68fb28c3e8" + ], + "markers": "python_version >= '3.8'", + "version": "==4.0.1" }, "jeepney": { "hashes": [ @@ -744,61 +1176,20 @@ }, "jinja2": { "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" + "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", + "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" ], + "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==3.1.2" + "version": "==3.1.6" }, "keyring": { "hashes": [ - "sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6", - "sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509" + "sha256:2458681cdefc0dbc0b7eb6cf75d0b98e59f9ad9b2d4edd319d18f68bdca95e50", + "sha256:daaffd42dbda25ddafb1ad5fec4024e5bbcfe424597ca1ca452b299861e49f1b" ], "markers": "python_version >= '3.8'", - "version": "==24.2.0" - }, - "lazy-object-proxy": { - "hashes": [ - "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", - "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", - "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", - "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", - "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", - "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", - "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", - "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", - "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", - "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", - "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", - "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", - "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", - "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", - "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", - "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", - "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", - "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", - "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", - "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", - "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", - "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", - "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", - "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", - "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", - "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", - "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", - "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", - "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", - "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", - "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", - "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", - "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", - "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", - "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", - "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.0" + "version": "==25.2.1" }, "markdown-it-py": { "hashes": [ @@ -810,59 +1201,70 @@ }, "markupsafe": { "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.3" + "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", + "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", + "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", + "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", + "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", + "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", + "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", + "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", + "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", + "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", + "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", + "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", + "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", + "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", + "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", + "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", + "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", + "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", + "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", + "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", + "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", + "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", + "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", + "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", + "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", + "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", + "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", + "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", + "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", + "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", + "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", + "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", + "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", + "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", + "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", + "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", + "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", + "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", + "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", + "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", + "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", + "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", + "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", + "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", + "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", + "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", + "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", + "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", + "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", + "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", + "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", + "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", + "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", + "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", + "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", + "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", + "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", + "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", + "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", + "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", + "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" + ], + "markers": "python_version >= '3.9'", + "version": "==3.0.2" }, "mccabe": { "hashes": [ @@ -882,11 +1284,11 @@ }, "more-itertools": { "hashes": [ - "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d", - "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3" + "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684", + "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1" ], - "markers": "python_version >= '3.7'", - "version": "==9.1.0" + "markers": "python_version >= '3.8'", + "version": "==10.2.0" }, "mypy-extensions": { "hashes": [ @@ -896,154 +1298,193 @@ "markers": "python_version >= '3.5'", "version": "==1.0.0" }, + "nh3": { + "hashes": [ + "sha256:0316c25b76289cf23be6b66c77d3608a4fdf537b35426280032f432f14291b9a", + "sha256:1a814dd7bba1cb0aba5bcb9bebcc88fd801b63e21e2450ae6c52d3b3336bc911", + "sha256:1aa52a7def528297f256de0844e8dd680ee279e79583c76d6fa73a978186ddfb", + "sha256:22c26e20acbb253a5bdd33d432a326d18508a910e4dcf9a3316179860d53345a", + "sha256:40015514022af31975c0b3bca4014634fa13cb5dc4dbcbc00570acc781316dcc", + "sha256:40d0741a19c3d645e54efba71cb0d8c475b59135c1e3c580f879ad5514cbf028", + "sha256:551672fd71d06cd828e282abdb810d1be24e1abb7ae2543a8fa36a71c1006fe9", + "sha256:66f17d78826096291bd264f260213d2b3905e3c7fae6dfc5337d49429f1dc9f3", + "sha256:85cdbcca8ef10733bd31f931956f7fbb85145a4d11ab9e6742bbf44d88b7e351", + "sha256:a3f55fabe29164ba6026b5ad5c3151c314d136fd67415a17660b4aaddacf1b10", + "sha256:b4427ef0d2dfdec10b641ed0bdaf17957eb625b2ec0ea9329b3d28806c153d71", + "sha256:ba73a2f8d3a1b966e9cdba7b211779ad8a2561d2dba9674b8a19ed817923f65f", + "sha256:c21bac1a7245cbd88c0b0e4a420221b7bfa838a2814ee5bb924e9c2f10a1120b", + "sha256:c551eb2a3876e8ff2ac63dff1585236ed5dfec5ffd82216a7a174f7c5082a78a", + "sha256:c790769152308421283679a142dbdb3d1c46c79c823008ecea8e8141db1a2062", + "sha256:d7a25fd8c86657f5d9d576268e3b3767c5cd4f42867c9383618be8517f0f022a" + ], + "version": "==0.2.17" + }, "packaging": { "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" ], "markers": "python_version >= '3.7'", - "version": "==23.1" + "version": "==24.0" }, "pathspec": { "hashes": [ - "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687", - "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293" + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" ], - "markers": "python_version >= '3.7'", - "version": "==0.11.1" + "markers": "python_version >= '3.8'", + "version": "==0.12.1" }, "pbr": { "hashes": [ - "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b", - "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3" + "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24", + "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a" ], "markers": "python_version >= '2.6'", - "version": "==5.11.1" + "version": "==6.1.0" }, "pkginfo": { "hashes": [ - "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546", - "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046" + "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297", + "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097" ], "markers": "python_version >= '3.6'", - "version": "==1.9.6" + "version": "==1.10.0" }, "platformdirs": { "hashes": [ - "sha256:cec7b889196b9144d088e4c57d9ceef7374f6c39694ad1577a0aab50d27ea28c", - "sha256:f87ca4fcff7d2b0f81c6a748a77973d7af0f4d526f98f308477c3c436c74d528" + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" ], - "markers": "python_version >= '3.7'", - "version": "==3.8.1" + "markers": "python_version >= '3.8'", + "version": "==4.3.6" }, "pluggy": { "hashes": [ - "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", - "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], - "markers": "python_version >= '3.7'", - "version": "==1.2.0" + "markers": "python_version >= '3.8'", + "version": "==1.5.0" }, "pycparser": { "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" ], - "version": "==2.21" + "markers": "python_version >= '3.8'", + "version": "==2.22" }, "pygments": { "hashes": [ - "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c", - "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1" + "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", + "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c" ], - "markers": "python_version >= '3.7'", - "version": "==2.15.1" + "markers": "python_version >= '3.8'", + "version": "==2.19.1" }, "pylint": { "hashes": [ - "sha256:eb035800b371862e783f27067b3fc00c6b726880cacfed101b619f366bb813f6", - "sha256:f0b0857f6fba90527a30f39937a9f66858b59c5dcbb8c062821ad665637bb742" + "sha256:289e6a1eb27b453b08436478391a48cd53bb0efb824873f949e709350f3de018", + "sha256:74ae7a38b177e69a9b525d0794bd8183820bfa7eb68cc1bee6e8ed22a42be4ce" ], "index": "pypi", - "version": "==3.0.0a6" + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.4" }, "pytest": { "hashes": [ - "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", - "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" + "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd", + "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1" ], "index": "pypi", - "version": "==7.4.0" + "markers": "python_version >= '3.8'", + "version": "==8.2.1" }, "pytest-mock": { "hashes": [ - "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39", - "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f" + "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", + "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0" ], "index": "pypi", - "version": "==3.11.1" + "markers": "python_version >= '3.8'", + "version": "==3.14.0" }, "pyyaml": { "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" ], - "markers": "python_version >= '3.6'", - "version": "==6.0" + "markers": "python_version >= '3.8'", + "version": "==6.0.2" }, "readme-renderer": { "hashes": [ - "sha256:9f77b519d96d03d7d7dce44977ba543090a14397c4f60de5b6eb5b8048110aa4", - "sha256:e18feb2a1e7706f2865b81ebb460056d93fb29d69daa10b223c00faa7bd9a00a" + "sha256:1818dd28140813509eeed8d62687f7cd4f7bad90d4db586001c5dc09d4fde311", + "sha256:19db308d86ecd60e5affa3b2a98f017af384678c63c88e5d4556a380e674f3f9" ], "markers": "python_version >= '3.8'", - "version": "==40.0" + "version": "==43.0" }, "requests": { "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", + "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422" ], - "markers": "python_version >= '3.7'", - "version": "==2.31.0" + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.32.4" }, "requests-toolbelt": { "hashes": [ @@ -1063,11 +1504,11 @@ }, "rich": { "hashes": [ - "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec", - "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898" + "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", + "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==13.4.2" + "markers": "python_full_version >= '3.8.0'", + "version": "==13.9.4" }, "secretstorage": { "hashes": [ @@ -1079,27 +1520,12 @@ }, "setuptools": { "hashes": [ - "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", - "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235" + "sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561", + "sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d" ], "index": "pypi", - "version": "==68.0.0" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "smmap": { - "hashes": [ - "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94", - "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936" - ], - "markers": "python_version >= '3.6'", - "version": "==5.0.0" + "markers": "python_version >= '3.9'", + "version": "==78.1.1" }, "snowballstemmer": { "hashes": [ @@ -1110,35 +1536,36 @@ }, "sphinx": { "hashes": [ - "sha256:60c5e04756c1709a98845ed27a2eed7a556af3993afb66e77fec48189f742616", - "sha256:61e025f788c5977d9412587e733733a289e2b9fdc2fef8868ddfbfc4ccfe881d" + "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3", + "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc" ], "index": "pypi", - "version": "==7.0.1" + "markers": "python_version >= '3.9'", + "version": "==7.3.7" }, "sphinxcontrib-applehelp": { "hashes": [ - "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228", - "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e" + "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619", + "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4" ], - "markers": "python_version >= '3.8'", - "version": "==1.0.4" + "markers": "python_version >= '3.9'", + "version": "==1.0.8" }, "sphinxcontrib-devhelp": { "hashes": [ - "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e", - "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4" + "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f", + "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3" ], - "markers": "python_version >= '3.5'", - "version": "==1.0.2" + "markers": "python_version >= '3.9'", + "version": "==1.0.6" }, "sphinxcontrib-htmlhelp": { "hashes": [ - "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff", - "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903" + "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015", + "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04" ], - "markers": "python_version >= '3.8'", - "version": "==2.0.1" + "markers": "python_version >= '3.9'", + "version": "==2.0.5" }, "sphinxcontrib-jsmath": { "hashes": [ @@ -1150,27 +1577,27 @@ }, "sphinxcontrib-qthelp": { "hashes": [ - "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", - "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6" + "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6", + "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182" ], - "markers": "python_version >= '3.5'", - "version": "==1.0.3" + "markers": "python_version >= '3.9'", + "version": "==1.0.7" }, "sphinxcontrib-serializinghtml": { "hashes": [ - "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd", - "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952" + "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7", + "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f" ], - "markers": "python_version >= '3.5'", - "version": "==1.1.5" + "markers": "python_version >= '3.9'", + "version": "==1.1.10" }, "stevedore": { "hashes": [ - "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d", - "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c" + "sha256:79e92235ecb828fe952b6b8b0c6c87863248631922c8e8e0fa5b17b232c4514d", + "sha256:b0be3c4748b3ea7b854b265dcb4caa891015e442416422be16f8b31756107857" ], - "markers": "python_version >= '3.8'", - "version": "==5.1.0" + "markers": "python_version >= '3.9'", + "version": "==5.4.0" }, "tomli": { "hashes": [ @@ -1182,139 +1609,55 @@ }, "tomlkit": { "hashes": [ - "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171", - "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3" + "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", + "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79" ], - "markers": "python_version >= '3.7'", - "version": "==0.11.8" + "markers": "python_version >= '3.8'", + "version": "==0.13.2" }, "twine": { "hashes": [ - "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8", - "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8" + "sha256:4d74770c88c4fcaf8134d2a6a9d863e40f08255ff7d8e2acb3cbbd57d25f6e9d", + "sha256:fe1d814395bfe50cfbe27783cb74efe93abeac3f66deaeb6c8390e4e92bacb43" ], "index": "pypi", - "version": "==4.0.2" + "markers": "python_version >= '3.8'", + "version": "==5.1.0" }, "typing-extensions": { "hashes": [ - "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", - "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2" + "sha256:be199d06d8f09ca2c9425e3aa04a9afba33e892fe079dea959e72df7f8442343", + "sha256:f933a7b288a919ca97adbff656e52ff81f7ff25d98a2aabb9355ca4090f772fe" ], - "markers": "python_version >= '3.7'", - "version": "==4.7.1" + "markers": "python_version < '3.11'", + "version": "==4.12.0rc1" }, "urllib3": { "hashes": [ - "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1", - "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825" + "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f", + "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.3" - }, - "webencodings": { - "hashes": [ - "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", - "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" - ], - "version": "==0.5.1" + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==2.6.0" }, "wheel": { "hashes": [ - "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873", - "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247" + "sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85", + "sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81" ], "index": "pypi", - "version": "==0.40.0" - }, - "wrapt": { - "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version < '3.11'", - "version": "==1.15.0" + "markers": "python_version >= '3.8'", + "version": "==0.43.0" }, "zipp": { "hashes": [ - "sha256:1876cb065531855bbe83b6c489dcf69ecc28f1068d8e95959fe8bbc77774c941", - "sha256:5dadc3ad0a1f825fe42ce1bce0f2fc5a13af2e6b2d386af5b0ff295bc0a287d3" + "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091", + "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f" ], + "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==3.16.0" + "version": "==3.19.1" } } } diff --git a/clients/python/README.md b/clients/python/README.md index 6a1f0b1ed..809173399 100644 --- a/clients/python/README.md +++ b/clients/python/README.md @@ -166,7 +166,7 @@ Supposing all your domains, except "foo.bar", properly implement DMARC: ### Build Docs -The client comes with documentation generated by [Sphinx](https://www.sphinx-doc.org/en/3.x/index.html) from the docstrings in the source code. Because of this, they are not included in the Github repo. To build the docs you will first need to install Sphinx. +The client comes with documentation generated by [Sphinx](https://www.sphinx-doc.org/en/3.x/index.html) from the docstrings in the source code. Because of this, they are not included in the GitHub repo. To build the docs you will first need to install Sphinx. The simplest way to ensure you have Sphinx installed is to install the client's dev dependencies with pipenv: diff --git a/clients/python/cloudbuild.yaml b/clients/python/cloudbuild.yaml index e8e7b5b6e..34ab0b38c 100644 --- a/clients/python/cloudbuild.yaml +++ b/clients/python/cloudbuild.yaml @@ -1,5 +1,5 @@ steps: - - name: 'gcr.io/track-compliance/ci' + - name: 'northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/ci' id: install-client-deps dir: clients/python entrypoint: pipenv @@ -7,13 +7,13 @@ steps: env: - PIPENV_NOSPIN=TRUE - - name: 'gcr.io/track-compliance/ci' + - name: 'northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/ci' id: lint-client dir: clients/python entrypoint: /bin/sh args: ['-c', 'pipenv run black --check tracker_client/ && pipenv run bandit -r tracker_client/'] - - name: 'gcr.io/track-compliance/ci' + - name: 'northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/ci' id: test-client dir: clients/python entrypoint: pipenv @@ -26,4 +26,4 @@ steps: timeout: 1200s options: machineType: 'E2_HIGHCPU_8' - + diff --git a/clients/python/tracker_client/__version__.py b/clients/python/tracker_client/__version__.py index 7018faa66..52ebd469c 100644 --- a/clients/python/tracker_client/__version__.py +++ b/clients/python/tracker_client/__version__.py @@ -1,2 +1,3 @@ """Single source of truth for versioning""" + __version__ = "1.0.0-alpha.1" diff --git a/clients/python/tracker_client/client.py b/clients/python/tracker_client/client.py index 72e1db2b7..0ef5bee5d 100644 --- a/clients/python/tracker_client/client.py +++ b/clients/python/tracker_client/client.py @@ -1,4 +1,5 @@ """This module defines the Client class, used to connect to the Tracker API.""" + from slugify import slugify from gql.transport.exceptions import ( TransportQueryError, diff --git a/clients/python/tracker_client/core.py b/clients/python/tracker_client/core.py index 5a5ce417b..824eeac6b 100644 --- a/clients/python/tracker_client/core.py +++ b/clients/python/tracker_client/core.py @@ -1,12 +1,13 @@ """This module provides utility functions related to gql, for internal use.""" + import os import re from gql import Client from gql.transport.aiohttp import AIOHTTPTransport -from queries import SIGNIN_MUTATION, TFA_AUTH -from __version__ import __version__ +from tracker_client.queries import SIGNIN_MUTATION, TFA_AUTH +from tracker_client.__version__ import __version__ _JWT_RE = r"^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$" diff --git a/clients/python/tracker_client/domain.py b/clients/python/tracker_client/domain.py index faa35e01c..0ccb5bbf6 100644 --- a/clients/python/tracker_client/domain.py +++ b/clients/python/tracker_client/domain.py @@ -1,5 +1,6 @@ """This module defines the Domain class, which models domains monitored by Tracker and offers methods to get data about domains.""" + import json from tracker_client import organization as org diff --git a/clients/python/tracker_client/organization.py b/clients/python/tracker_client/organization.py index 2d580e487..c5d2cc80c 100644 --- a/clients/python/tracker_client/organization.py +++ b/clients/python/tracker_client/organization.py @@ -1,5 +1,6 @@ """This module defines the Domain class, which models organizations monitored by Tracker and offers methods to get data about them.""" + import json from slugify import slugify diff --git a/database-migration/.dockerignore b/database-migration/.dockerignore index 3c395b0e3..fb8845991 100644 --- a/database-migration/.dockerignore +++ b/database-migration/.dockerignore @@ -2,4 +2,3 @@ node_modules Dockerfile README.md -database.json diff --git a/database-migration/.env.example b/database-migration/.env.example index 8ca879b11..b30ff0ef2 100644 --- a/database-migration/.env.example +++ b/database-migration/.env.example @@ -3,4 +3,3 @@ DB_USER= DB_PASS= DB_NAME= ROOT_PASS= -DB_DESCRIPTION= diff --git a/database-migration/Dockerfile b/database-migration/Dockerfile index 243026963..a7dd9c919 100644 --- a/database-migration/Dockerfile +++ b/database-migration/Dockerfile @@ -1,4 +1,4 @@ -FROM node:alpine +FROM node:25.3.0-alpine ENV NODE_ENV production @@ -8,6 +8,7 @@ COPY package.json . COPY package-lock.json . COPY .env.example . COPY index.js . +COPY database.json . RUN npm ci diff --git a/database-migration/cloudbuild.yaml b/database-migration/cloudbuild.yaml new file mode 100644 index 000000000..3d1c79128 --- /dev/null +++ b/database-migration/cloudbuild.yaml @@ -0,0 +1,49 @@ +steps: + - name: node:alpine + id: install + dir: database-migration + entrypoint: npm + args: ['ci', '--no-optional'] + + - name: node:alpine + id: lint + dir: database-migration + entrypoint: npm + args: ['run', lint] + + - name: 'gcr.io/cloud-builders/docker' + id: generate-image-name + entrypoint: 'bash' + dir: database-migration + args: + - '-c' + - | + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/database-migration:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename + + - name: 'gcr.io/cloud-builders/docker' + id: build + entrypoint: 'bash' + dir: database-migration + args: + - '-c' + - | + image=$(cat /workspace/imagename) + docker build -t $image . + + - name: 'gcr.io/cloud-builders/docker' + id: push-if-master + entrypoint: 'bash' + args: + - '-c' + - | + if [[ "$BRANCH_NAME" == "master" ]] + then + image=$(cat /workspace/imagename) + docker push $image + else + exit 0 + fi + +timeout: 1200s +options: + machineType: 'E2_HIGHCPU_8' diff --git a/database-migration/database.json b/database-migration/database.json index ebf5a0965..05e7ef27c 100644 --- a/database-migration/database.json +++ b/database-migration/database.json @@ -77,6 +77,15 @@ "numberOfShards": 6 } }, + { + "type": "documentcollection", + "name": "guidanceTags", + "options": { + "replicationFactor": 3, + "writeConcern": 1, + "numberOfShards": 6 + } + }, { "type": "documentcollection", "name": "dkimGuidanceTags", @@ -176,7 +185,7 @@ "numberofshards": 6 } }, - { + { "type": "documentcollection", "name": "organizationSummaries", "options": { @@ -185,6 +194,24 @@ "numberofshards": 6 } }, + { + "type": "documentcollection", + "name": "additionalFindings", + "options": { + "replicationfactor": 3, + "writeconcern": 1, + "numberofshards": 6 + } + }, + { + "type": "documentcollection", + "name": "tags", + "options": { + "replicationfactor": 3, + "writeconcern": 1, + "numberofshards": 6 + } + }, { "type": "edgecollection", "name": "domainsToSelectors", diff --git a/database-migration/index.js b/database-migration/index.js index e7226107e..cb3bd2286 100644 --- a/database-migration/index.js +++ b/database-migration/index.js @@ -1,14 +1,35 @@ const { config } = require('dotenv-safe') const { ensure } = require('arango-tools') +const { Database } = require('arangojs') config() -const { DB_DESCRIPTION, DB_NAME, ROOT_PASS, DB_USER, DB_PASS, DB_URL } = - process.env +const { DB_DESCRIPTION = './database.json', DB_NAME, ROOT_PASS, DB_USER, DB_PASS, DB_URL } = process.env const schema = require(DB_DESCRIPTION) ;(async () => { + const systemDatabase = new Database({ url: DB_URL, databaseName: '_system' }) + await systemDatabase.login('root', ROOT_PASS) + const databases = await systemDatabase.listDatabases() + if (!databases.includes(DB_NAME)) { + console.log(`Tracker database ${DB_NAME} does not exist. Creating it.`) + try { + await systemDatabase.createDatabase(DB_NAME, { + users: [ + { + username: DB_USER, + passwd: DB_PASS, + active: true, + }, + ], + }) + } catch (e) { + console.error(`Failed to create database ${DB_NAME}: ${e.message}`) + process.exit(1) + } + } + await ensure({ variables: { rootPassword: ROOT_PASS, diff --git a/database-migration/package-lock.json b/database-migration/package-lock.json index 521eca0f3..5a3668bf9 100644 --- a/database-migration/package-lock.json +++ b/database-migration/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "arango-tools": "^0.6.0", + "arangojs": "^9.2.0", "dotenv-safe": "^8.2.0", "json-placeholder-replacer": "^1.0.35" }, @@ -122,9 +123,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", - "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==" + "version": "20.19.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", + "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", + "dependencies": { + "undici-types": "~6.21.0" + } }, "node_modules/acorn": { "version": "8.8.1", @@ -197,7 +201,7 @@ "json-placeholder-replacer": "^1.0.35" } }, - "node_modules/arangojs": { + "node_modules/arango-tools/node_modules/arangojs": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-7.8.0.tgz", "integrity": "sha512-aJFlMKlVr4sIO5GDMuykBVNVxWeZTkWDgYbbl9cIuxVctp8Lhs6dW5fr5MYlwAndnOEyi3bvbrhZIucly2IpWQ==", @@ -212,6 +216,49 @@ "node": ">=10" } }, + "node_modules/arango-tools/node_modules/file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/arango-tools/node_modules/mime-kind": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-3.0.0.tgz", + "integrity": "sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ==", + "dependencies": { + "file-type": "^12.1.0", + "mime-types": "^2.1.24" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/arango-tools/node_modules/multi-part": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-3.0.0.tgz", + "integrity": "sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw==", + "dependencies": { + "mime-kind": "^3.0.0", + "multi-part-lite": "^1.0.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/arangojs": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-9.2.0.tgz", + "integrity": "sha512-WIoFPCrBeuX1kBzGwue4t+jjYFW/ipto4ZO5s6e1v6dMsZE4I+d5Bid2glL+D5UeUJ6qGgqfe3KOAAW1t0Pc2A==", + "dependencies": { + "@types/node": "^20.11.26" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1050,14 +1097,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-type": { - "version": "12.4.2", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", - "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1088,9 +1127,9 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "node_modules/fs.realpath": { @@ -1585,9 +1624,9 @@ } }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "dependencies": { "argparse": "^2.0.1" @@ -1684,18 +1723,6 @@ "node": ">= 0.6" } }, - "node_modules/mime-kind": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-3.0.0.tgz", - "integrity": "sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ==", - "dependencies": { - "file-type": "^12.1.0", - "mime-types": "^2.1.24" - }, - "engines": { - "node": ">=8.3.0" - } - }, "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", @@ -1708,17 +1735,17 @@ } }, "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", "dependencies": { "dom-walk": "^0.1.0" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -1742,18 +1769,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/multi-part": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-3.0.0.tgz", - "integrity": "sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw==", - "dependencies": { - "mime-kind": "^3.0.0", - "multi-part-lite": "^1.0.0" - }, - "engines": { - "node": ">=8.3.0" - } - }, "node_modules/multi-part-lite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/multi-part-lite/-/multi-part-lite-1.0.0.tgz", @@ -2298,6 +2313,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2474,9 +2494,12 @@ "dev": true }, "@types/node": { - "version": "18.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", - "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==" + "version": "20.19.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", + "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", + "requires": { + "undici-types": "~6.21.0" + } }, "acorn": { "version": "8.8.1", @@ -2526,18 +2549,51 @@ "arangojs": "^7.2.0", "assign-deep": "^1.0.1", "json-placeholder-replacer": "^1.0.35" + }, + "dependencies": { + "arangojs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-7.8.0.tgz", + "integrity": "sha512-aJFlMKlVr4sIO5GDMuykBVNVxWeZTkWDgYbbl9cIuxVctp8Lhs6dW5fr5MYlwAndnOEyi3bvbrhZIucly2IpWQ==", + "requires": { + "@types/node": ">=13.13.4", + "es6-error": "^4.0.1", + "multi-part": "^3.0.0", + "x3-linkedlist": "1.2.0", + "xhr": "^2.4.1" + } + }, + "file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==" + }, + "mime-kind": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-3.0.0.tgz", + "integrity": "sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ==", + "requires": { + "file-type": "^12.1.0", + "mime-types": "^2.1.24" + } + }, + "multi-part": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-3.0.0.tgz", + "integrity": "sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw==", + "requires": { + "mime-kind": "^3.0.0", + "multi-part-lite": "^1.0.0" + } + } } }, "arangojs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-7.8.0.tgz", - "integrity": "sha512-aJFlMKlVr4sIO5GDMuykBVNVxWeZTkWDgYbbl9cIuxVctp8Lhs6dW5fr5MYlwAndnOEyi3bvbrhZIucly2IpWQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/arangojs/-/arangojs-9.2.0.tgz", + "integrity": "sha512-WIoFPCrBeuX1kBzGwue4t+jjYFW/ipto4ZO5s6e1v6dMsZE4I+d5Bid2glL+D5UeUJ6qGgqfe3KOAAW1t0Pc2A==", "requires": { - "@types/node": ">=13.13.4", - "es6-error": "^4.0.1", - "multi-part": "^3.0.0", - "x3-linkedlist": "1.2.0", - "xhr": "^2.4.1" + "@types/node": "^20.11.26" } }, "argparse": { @@ -3151,11 +3207,6 @@ "flat-cache": "^3.0.4" } }, - "file-type": { - "version": "12.4.2", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", - "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==" - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3177,9 +3228,9 @@ } }, "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "fs.realpath": { @@ -3526,9 +3577,9 @@ "dev": true }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -3600,15 +3651,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, - "mime-kind": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-3.0.0.tgz", - "integrity": "sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ==", - "requires": { - "file-type": "^12.1.0", - "mime-types": "^2.1.24" - } - }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", @@ -3618,17 +3660,17 @@ } }, "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", "requires": { "dom-walk": "^0.1.0" } }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -3646,15 +3688,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "multi-part": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-3.0.0.tgz", - "integrity": "sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw==", - "requires": { - "mime-kind": "^3.0.0", - "multi-part-lite": "^1.0.0" - } - }, "multi-part-lite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/multi-part-lite/-/multi-part-lite-1.0.0.tgz", @@ -4017,6 +4050,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/database-migration/package.json b/database-migration/package.json index 22d971bbf..47fbdedad 100644 --- a/database-migration/package.json +++ b/database-migration/package.json @@ -4,7 +4,7 @@ "description": "creates the database according to a setup file", "main": "index.js", "scripts": { - "lint": "eslint src", + "lint": "eslint", "start": "node --dns-result-order=ipv4first --experimental-json-modules --experimental-vm-modules index.js" }, "keywords": [], @@ -12,6 +12,7 @@ "license": "MIT", "dependencies": { "arango-tools": "^0.6.0", + "arangojs": "^9.2.0", "dotenv-safe": "^8.2.0", "json-placeholder-replacer": "^1.0.35" }, diff --git a/frontend-maintenance/cloudbuild.yaml b/frontend-maintenance/cloudbuild.yaml index 2455556ac..df4af9bc4 100644 --- a/frontend-maintenance/cloudbuild.yaml +++ b/frontend-maintenance/cloudbuild.yaml @@ -1,33 +1,28 @@ steps: - - name: 'gcr.io/cloud-builders/docker' + - name: "gcr.io/cloud-builders/docker" id: generate-image-name - entrypoint: 'bash' + entrypoint: "bash" dir: frontend-maintenance args: - - '-c' + - "-c" - | - echo "gcr.io/$PROJECT_ID/frontend-maintenance:$BRANCH_NAME-$SHORT_SHA-$(date +%s)" > /workspace/imagename + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/frontend-maintenance:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename - - name: 'gcr.io/cloud-builders/docker' - id: build-if-master - entrypoint: 'bash' + - name: "gcr.io/cloud-builders/docker" + id: build + entrypoint: "bash" dir: frontend-maintenance args: - - '-c' + - "-c" - | - if [[ "$BRANCH_NAME" == "master" ]] - then - image=$(cat /workspace/imagename) - docker build -t $image . - else - exit 0 - fi + image=$(cat /workspace/imagename) + docker build -t $image . - - name: 'gcr.io/cloud-builders/docker' + - name: "gcr.io/cloud-builders/docker" id: push-if-master - entrypoint: 'bash' + entrypoint: "bash" args: - - '-c' + - "-c" - | if [[ "$BRANCH_NAME" == "master" ]] then @@ -39,4 +34,4 @@ steps: timeout: 1200s options: - machineType: 'E2_HIGHCPU_8' + machineType: "E2_HIGHCPU_8" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 7aad2a6e9..96809bf10 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:17-alpine as build-env +FROM node:20.18.1-alpine3.19 as build-env WORKDIR /app @@ -7,9 +7,7 @@ COPY . . RUN npm ci && npm run build && npm prune --production -# https://github.com/astefanutti/scratch-node -FROM astefanutti/scratch-node:17 -LABEL maintainer="mike.williamson@tbs-sct.gc.ca" +FROM node:20.18.1-alpine3.19 ENV HOST 0.0.0.0 ENV PORT 3000 @@ -22,8 +20,7 @@ ENV NODE_ENV production # https://github.com/webpack/webpack/issues/14532#issuecomment-947012063 ENV NODE_OPTIONS=--openssl-legacy-provider -USER node +USER nonroot EXPOSE 3000 -ENTRYPOINT ["/bin/node"] CMD ["index.js"] diff --git a/frontend/README.md b/frontend/README.md index 67610e229..adc9c9e7c 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -57,7 +57,7 @@ This allows changes to the code to be reflected immediately in the running conta You can see the service running on `localhost:3000` and the API running on `localhost:3000/graphql`. If you want to modify the schema you can reach the editor at `localhost:3000/editor`. -#### Stopping it it +#### Stopping When you are done: diff --git a/frontend/cloudbuild.yaml b/frontend/cloudbuild.yaml index 7fb1ab45e..0c68fb760 100644 --- a/frontend/cloudbuild.yaml +++ b/frontend/cloudbuild.yaml @@ -1,63 +1,78 @@ steps: - - name: node:16.3.0-alpine + - name: node:20.10.0-alpine id: install dir: frontend entrypoint: npm args: ['ci', '--no-optional'] - - name: node:16.3.0-alpine + - name: node:20.10.0-alpine id: lint dir: frontend entrypoint: npm args: ['run', 'lint'] - - name: node:16.3.0-alpine - id: test + - name: node:20.10.0-alpine + id: lingui-extract dir: frontend - entrypoint: npm - args: ['test'] + entrypoint: ash + args: + - '-c' + - | + npm run extract | tee /workspace/lingui-extract-output.txt - - name: node:16.3.0-alpine - id: lingui-extract + - name: node:20.10.0-alpine + id: lingui-translation-check dir: frontend - entrypoint: npm - args: ['run', 'extract'] + entrypoint: ash + args: + - '-c' + - | + output=$( awk -F '│' '/fr/ { gsub(/^[ \t]+|[ \t]+$/, "", $4); print $4 }' /workspace/lingui-extract-output.txt ) - - name: node:16.3.0-alpine + # Check if there are any missed french translations + if [ "$output" -eq 0 ]; then + echo "All translations are up to date, proceeding." + else + echo "There are $output missed translations, please update them before proceeding." + exit 1 + fi + + - name: node:20.10.0-alpine id: lingui-compile dir: frontend entrypoint: npm args: ['run', 'compile'] - - name: node:16.3.0-alpine + - name: node:20.10.0-alpine id: build-production-bundle dir: frontend entrypoint: npm - args: ['run', 'build'] + args: [ 'run', 'build' ] + + - name: node:20.10.0-alpine + id: test + dir: frontend + entrypoint: npm + args: ['test'] - name: 'gcr.io/cloud-builders/docker' id: generate-image-name entrypoint: 'bash' - dir: api-js + dir: frontend args: - '-c' - | - echo "gcr.io/$PROJECT_ID/frontend:$BRANCH_NAME-$SHORT_SHA-$(date +%s)" > /workspace/imagename + echo "northamerica-northeast1-docker.pkg.dev/track-compliance/tracker/frontend:$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9]/-/g')-$SHORT_SHA-$(date +%s)" > /workspace/imagename - name: 'gcr.io/cloud-builders/docker' - id: build-if-master + id: build entrypoint: 'bash' dir: frontend args: - '-c' - | - if [[ "$BRANCH_NAME" == "master" ]] - then - image=$(cat /workspace/imagename) - docker build -t $image . - else - exit 0 - fi + image=$(cat /workspace/imagename) + docker build -t $image . - name: 'gcr.io/cloud-builders/docker' id: push-if-master diff --git a/frontend/docker-compose.yaml b/frontend/docker-compose.yaml index 06e8ce26f..e2594e3d1 100644 --- a/frontend/docker-compose.yaml +++ b/frontend/docker-compose.yaml @@ -1,9 +1,11 @@ version: "3.7" services: envoy: # proxies localhost:3000 to containers based on rules in config - image: envoyproxy/envoy-alpine-dev + image: envoyproxy/envoy-alpine:v1.21.6 + network_mode: "host" entrypoint: envoy command: ["-c", "/etc/envoy-dev.yaml", "--service-cluster", "envoy"] + restart: always volumes: - ./envoy-dev.yaml:/etc/envoy-dev.yaml # Envoy configuration expose: @@ -12,25 +14,6 @@ services: ports: - "3000:3000" - "3001:3001" - frontend: # localhost:3000/* is sent to frontend - image: node:16.3.0-alpine - working_dir: /app - command: npm run dev - volumes: - - ./:/app - expose: - - "3000" - mocked_api: # available at localhost:3000/graphql - image: node:16.3.0-alpine - working_dir: /app - command: npm run mocker - volumes: - - ./mocking/:/app/mocking - - ./node_modules/:/app/node_modules - - ./package.json:/app/package.json - - ./.babelrc:/app/.babelrc - expose: - - "4000" volumes: driver: {} diff --git a/frontend/envoy-dev.yaml b/frontend/envoy-dev.yaml index 58c4fecc6..842d5ce57 100644 --- a/frontend/envoy-dev.yaml +++ b/frontend/envoy-dev.yaml @@ -22,9 +22,9 @@ static_resources: domains: ["*"] routes: - match: { prefix: "/graphql" } - route: { cluster: mocked_api } + route: { cluster: api } - match: { prefix: "/editor" } - route: { cluster: mocked_api } + route: { cluster: api } - match: { prefix: "/" } route: { cluster: frontend } clusters: @@ -39,20 +39,20 @@ static_resources: - endpoint: address: socket_address: - address: frontend - port_value: 3000 - - name: mocked_api + address: 127.0.0.1 + port_value: 3300 + - name: api connect_timeout: 0.25s type: strict_dns lb_policy: round_robin load_assignment: - cluster_name: mocked_api + cluster_name: api endpoints: - lb_endpoints: - endpoint: address: socket_address: - address: mocked_api + address: 127.0.0.1 port_value: 4000 admin: access_log_path: "/dev/null" diff --git a/frontend/mocking/faked_schema.js b/frontend/mocking/faked_schema.js index 600f07119..cd1b399b2 100644 --- a/frontend/mocking/faked_schema.js +++ b/frontend/mocking/faked_schema.js @@ -2,3574 +2,6074 @@ import { gql } from '@apollo/client/core' export const getTypeNames = () => gql` type Query { - # Fetches an object given its ID + """ + Fetches an object given its ID + """ node( - # The ID of an object + """ + The ID of an object + """ id: ID! ): Node - # Fetches objects given their IDs + """ + Fetches objects given their IDs + """ nodes( - # The IDs of objects + """ + The IDs of objects + """ ids: [ID!]! ): [Node]! - # Select activity logs a user has access to. + """ + Select activity logs a user has access to. + """ findAuditLogs( - # The organization you wish to query the logs from. + """ + The organization you wish to query the logs from. + """ orgId: ID - # Ordering options for log connections. + """ + Ordering options for log connections. + """ orderBy: LogOrder - # String used to search for logs by initiant user or target resource. + """ + String used to search for logs by initiant user or target resource. + """ search: String - # Keywords used to filter log results. + """ + Keywords used to filter log results. + """ filters: LogFilters - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): AuditLogConnection - # Query for dmarc summaries the user has access to. + """ + Query for dmarc summaries the user has access to. + """ findMyDmarcSummaries( - # Ordering options for dmarc summaries connections + """ + Ordering options for dmarc summaries connections + """ orderBy: DmarcSummaryOrder - # The month in which the returned data is relevant to. + """ + The month in which the returned data is relevant to. + """ month: PeriodEnums! - # The year in which the returned data is relevant to. + """ + The year in which the returned data is relevant to. + """ year: Year! - # An optional string used to filter the results based on domains. + """ + An optional string used to filter the results based on domains. + """ search: String - # Returns the items in the list that come after the specified cursor. + """ + Filter the results based on the users affiliation. + """ + isAffiliated: Boolean + + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): DmarcSummaryConnection - # Retrieve a specific domain by providing a domain. + """ + Retrieve a specific domain by providing a domain. + """ findDomainByDomain( - # The domain you wish to retrieve information for. + """ + The domain you wish to retrieve information for. + """ domain: DomainScalar! ): Domain - # Select domains a user has access to. + """ + Select domains a user has access to. + """ findMyDomains( - # Ordering options for domain connections. + """ + Ordering options for domain connections. + """ orderBy: DomainOrder - # Limit domains to those that belong to an organization that has ownership. + """ + Limit domains to those that belong to an organization that has ownership. + """ ownership: Boolean - # String used to search for domains. + """ + String used to search for domains. + """ search: String - # Returns the items in the list that come after the specified cursor. + """ + Filter the results based on the users affiliation. + """ + isAffiliated: Boolean + + """ + Filters used to limit domains returned. + """ + filters: [DomainFilter] + + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): DomainConnection - # Select organizations a user has access to. + """ + Select organizations a user has access to. + """ findMyOrganizations( - # Ordering options for organization connections + """ + Ordering options for organization connections + """ orderBy: OrganizationOrder - # String argument used to search for organizations. + """ + String argument used to search for organizations. + """ search: String - # Filter orgs based off of the user being an admin of them. + """ + Filter orgs based off of the user being an admin of them. + """ isAdmin: Boolean - # Filter org list to either include or exclude the super admin org. + """ + Filter org list to either include or exclude the super admin org. + """ includeSuperAdminOrg: Boolean - # Filter org list to include only verified organizations. + """ + Filter org list to include only verified organizations. + """ isVerified: Boolean - # Returns the items in the list that come after the specified cursor. + """ + Filter the results based on the users affiliation. + """ + isAffiliated: Boolean + + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): OrganizationConnection - # Select all information on a selected organization that a user has access to. + """ + Select all information on a selected organization that a user has access to. + """ findOrganizationBySlug( - # The slugified organization name you want to retrieve data for. + """ + The slugified organization name you want to retrieve data for. + """ orgSlug: Slug! ): Organization - # CSV formatted output of all domains in all organizations including their email and web scan statuses. + """ + CSV formatted output of all domains in all organizations including their email and web scan statuses. + """ getAllOrganizationDomainStatuses( - # Whether to include blocked domains in the output. - blocked: Boolean + """ + Filters used to limit domains returned. + """ + filters: [DomainFilter] ): String - # DKIM summary computed values, used to build summary cards. + """ + DKIM summary computed values, used to build summary cards. + """ dkimSummary: CategorizedSummary - # DMARC phase summary computed values, used to build summary cards. + """ + DMARC phase summary computed values, used to build summary cards. + """ dmarcPhaseSummary: CategorizedSummary - # DMARC summary computed values, used to build summary cards. + """ + DMARC summary computed values, used to build summary cards. + """ dmarcSummary: CategorizedSummary - # HTTPS summary computed values, used to build summary cards. + """ + HTTPS summary computed values, used to build summary cards. + """ httpsSummary: CategorizedSummary - # Email summary computed values, used to build summary cards. + """ + Email summary computed values, used to build summary cards. + """ mailSummary: CategorizedSummary - # SPF summary computed values, used to build summary cards. + """ + SPF summary computed values, used to build summary cards. + """ spfSummary: CategorizedSummary - # SSL summary computed values, used to build summary cards. + """ + SSL summary computed values, used to build summary cards. + """ sslSummary: CategorizedSummary - # Web connections (HTTPS + HSTS) summary computed values, used to build summary cards. + """ + Web connections (HTTPS + HSTS) summary computed values, used to build summary cards. + """ webConnectionsSummary: CategorizedSummary - # Web summary computed values, used to build summary cards. + """ + Web summary computed values, used to build summary cards. + """ webSummary: CategorizedSummary - # Query the currently logged in user. + """ + Select domains a user has access to. + """ + findChartSummaries( + """ + The month in which the returned data is relevant to. + """ + month: PeriodEnums! + + """ + The year in which the returned data is relevant to. + """ + year: Year! + ): ChartSummaryConnection + + """ + Query the currently logged in user. + """ findMe: PersonalUser - # Select all information on a selected organization that a user has access to. + """ + Select all information on a selected organization that a user has access to. + """ findMyTracker: MyTrackerResult - # Select users an admin has access to. + """ + Select users an admin has access to. + """ findMyUsers( - # Ordering options for user affiliation - orderBy: AffiliationUserOrder - - # String used to search for users. + """ + Ordering options for user affiliation + """ + orderBy: UserOrder + + """ + String used to search for users. + """ search: String - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): UserConnection - # Query a specific user by user name. + """ + Query a specific user by user name. + """ findUserByUsername( - # Email address of user you wish to gather data for. + """ + Email address of user you wish to gather data for. + """ userName: EmailAddress! ): SharedUser - # Checks if user must be logged in to access data. + """ + Checks if user must be logged in to access data. + """ loginRequired: Boolean - # Query used to check if the user has an admin role. + """ + Query used to check if the user has an admin role. + """ isUserAdmin( - # Optional org id to see if user is an admin for the requested org. + """ + Optional org id to see if user is an admin for the requested org. + """ orgId: ID ): Boolean - # Query used to check if the user has a super admin role. + """ + Query used to check if the user has a super admin role. + """ isUserSuperAdmin: Boolean - # Retrieve a specific verified domain by providing a domain. + """ + Retrieve a specific verified domain by providing a domain. + """ findVerifiedDomainByDomain( - # The domain you wish to retrieve information for. + """ + The domain you wish to retrieve information for. + """ domain: DomainScalar! ): VerifiedDomain - # Select verified check domains + """ + Select verified check domains + """ findVerifiedDomains( - # Ordering options for verified domain connections. + """ + Ordering options for verified domain connections. + """ orderBy: VerifiedDomainOrder - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): VerifiedDomainConnection - # Select all information on a selected verified organization. + """ + Select all information on a selected verified organization. + """ findVerifiedOrganizationBySlug( - # The slugified organization name you want to retrieve data for. + """ + The slugified organization name you want to retrieve data for. + """ orgSlug: Slug! ): VerifiedOrganization - # Select organizations a user has access to. + """ + Select organizations a user has access to. + """ findVerifiedOrganizations( - # Ordering options for verified organization connections. + """ + Ordering options for verified organization connections. + """ orderBy: VerifiedOrganizationOrder - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): VerifiedOrganizationConnection } - # An object with an ID + """ + An object with an ID + """ interface Node { - # The id of the object. + """ + The id of the object. + """ id: ID! } - # A connection to a list of items. + """ + A connection to a list of items. + """ type AuditLogConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [AuditLogEdge] - # The total amount of logs the user has access to. + """ + The total amount of logs the user has access to. + """ totalCount: Int } - # Information about pagination in a connection. + """ + Information about pagination in a connection. + """ type PageInfo { - # When paginating forwards, are there more items? + """ + When paginating forwards, are there more items? + """ hasNextPage: Boolean! - # When paginating backwards, are there more items? + """ + When paginating backwards, are there more items? + """ hasPreviousPage: Boolean! - # When paginating backwards, the cursor to continue. + """ + When paginating backwards, the cursor to continue. + """ startCursor: String - # When paginating forwards, the cursor to continue. + """ + When paginating forwards, the cursor to continue. + """ endCursor: String } - # An edge in a connection. + """ + An edge in a connection. + """ type AuditLogEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: AuditLog - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # A record of activity that modified the state of a user, domain, or organization + """ + A record of activity that modified the state of a user, domain, or organization + """ type AuditLog implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Datetime string the activity occurred. + """ + Datetime string the activity occurred. + """ timestamp: DateTime - # Username of admin that initiated the activity. + """ + Username of admin that initiated the activity. + """ initiatedBy: InitiatedBy - # Type of activity that was initiated. + """ + Type of activity that was initiated. + """ action: UserActionEnums - # Information on targeted resource. + """ + Information on targeted resource. + """ target: TargetResource - # Optional reason for action, used for domain removal. + """ + Optional reason for action, used for domain removal. + """ reason: DomainRemovalReasonEnum } - # A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the 'date-time' format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. + """ + A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the 'date-time' format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. + """ scalar DateTime - # Information on the user that initiated the logged action + """ + Information on the user that initiated the logged action + """ type InitiatedBy { - # The ID of an object + """ + The ID of an object + """ id: ID! - # User email address. + """ + User email address. + """ userName: EmailAddress - # User permission level. + """ + User permission level. + """ role: RoleEnums - # User affiliated organization. + """ + User affiliated organization. + """ organization: String } - # A field whose value conforms to the standard internet email address format as specified in RFC822: https://www.w3.org/Protocols/rfc822/. + """ + A field whose value conforms to the standard internet email address format as specified in HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address. + """ scalar EmailAddress - # An enum used to assign, and test users roles. + """ + An enum used to assign, and test users roles. + """ enum RoleEnums { - # A user who has requested an invite to an organization. + """ + A user who has requested an invite to an organization. + """ PENDING - # A user who has been given access to view an organization. + """ + A user who has been given access to view an organization. + """ USER - # A user who has the same access as a user write account, but can define new user read/write accounts. + """ + A user who has the same access as a user write account, but can define new user read/write accounts. + """ ADMIN - # A user who has the same access as an admin, but can define new admins, and delete the organization. + """ + A user who has the same access as an admin, but can define new admins, and delete the organization. + """ OWNER - # A user who has the same access as an admin, but can define new admins. + """ + A user who has the same access as an admin, but can define new admins. + """ SUPER_ADMIN } - # Describes actions performed by users to modify resources. + """ + Describes actions performed by users to modify resources. + """ enum UserActionEnums { - # A new resource was created. + """ + A new resource was created. + """ CREATE - # A resource was deleted. + """ + A resource was deleted. + """ DELETE - # An affiliation between resources was created. + """ + An affiliation between resources was created. + """ ADD - # Properties of a resource or affiliation were modified. + """ + Properties of a resource or affiliation were modified. + """ UPDATE - # An affiliation between resources was deleted. + """ + An affiliation between resources was deleted. + """ REMOVE - # A scan was requested on a resource. + """ + A scan was requested on a resource. + """ SCAN - # A resource was exported. + """ + A resource was exported. + """ EXPORT } - # Resource that was the target of a specified action by a user. + """ + Resource that was the target of a specified action by a user. + """ type TargetResource { - # Name of the targeted resource. + """ + Name of the targeted resource. + """ resource: String - # Organization that the resource is affiliated with. + """ + Organization that the resource is affiliated with. + """ organization: TargetOrganization - # Type of resource that was modified: user, domain, or organization. + """ + Type of resource that was modified: user, domain, or organization. + """ resourceType: ResourceTypeEnums - # List of resource properties that were modified. + """ + List of resource properties that were modified. + """ updatedProperties: [UpdatedProperties] } - # Organization that the resource is affiliated with. + """ + Organization that the resource is affiliated with. + """ type TargetOrganization { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Name of the affiliated organization. + """ + Name of the affiliated organization. + """ name: String } - # Keywords used to decribe resources that can be modified. + """ + Keywords used to describe resources that can be modified. + """ enum ResourceTypeEnums { - # A user account affiliated with an organization. + """ + A user account affiliated with an organization. + """ USER - # An organization. + """ + An organization. + """ ORGANIZATION - # A domain affiliated with an organization. + """ + A domain affiliated with an organization. + """ DOMAIN } - # Object describing how a resource property was updated. + """ + Object describing how a resource property was updated. + """ type UpdatedProperties { - # Name of updated resource. + """ + Name of updated resource. + """ name: String - # Old value of updated property. + """ + Old value of updated property. + """ oldValue: String - # New value of updated property. + """ + New value of updated property. + """ newValue: String } - # Reason why a domain was removed from an organization. + """ + Reason why a domain was removed from an organization. + """ enum DomainRemovalReasonEnum { - # Domain does not exist. + """ + Domain does not exist. + """ NONEXISTENT - # Domain was in the incorrect organization. + """ + Domain was in the incorrect organization. + """ WRONG_ORG } - # Ordering options for audit logs. + """ + Ordering options for audit logs. + """ input LogOrder { - # The field to order logs by. + """ + The field to order logs by. + """ field: LogOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which domain connections can be ordered. + """ + Properties by which domain connections can be ordered. + """ enum LogOrderField { - # Order logs by timestamp. + """ + Order logs by timestamp. + """ TIMESTAMP - # Order logs by initiant's username. + """ + Order logs by initiant's username. + """ INITIATED_BY - # Order logs by name of targeted resource. + """ + Order logs by name of targeted resource. + """ RESOURCE_NAME } - # Possible directions in which to order a list of items when provided an 'orderBy' argument. + """ + Possible directions in which to order a list of items when provided an 'orderBy' argument. + """ enum OrderDirection { - # Specifies an ascending order for a given 'orderBy' argument. + """ + Specifies an ascending order for a given 'orderBy' argument. + """ ASC - # Specifies a descending order for a given 'orderBy' argument. + """ + Specifies a descending order for a given 'orderBy' argument. + """ DESC } - # Filtering options for audit logs. + """ + Filtering options for audit logs. + """ input LogFilters { - # List of resource types to include when returning logs. + """ + List of resource types to include when returning logs. + """ resource: [ResourceTypeEnums] - # List of user actions to include when returning logs. + """ + List of user actions to include when returning logs. + """ action: [UserActionEnums] } - # A connection to a list of items. + """ + A connection to a list of items. + """ type DmarcSummaryConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [DmarcSummaryEdge] - # The total amount of dmarc summaries the user has access to. + """ + The total amount of dmarc summaries the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type DmarcSummaryEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: DmarcSummary - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Object that contains information for a dmarc summary. + """ + Object that contains information for a dmarc summary. + """ type DmarcSummary implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # The domain that the data in this dmarc summary belongs to. + """ + The domain that the data in this dmarc summary belongs to. + """ domain: Domain - # Start date of data collection. + """ + Start date of data collection. + """ month: PeriodEnums - # End date of data collection. + """ + End date of data collection. + """ year: Year - # Category percentages based on the category totals. + """ + Category percentages based on the category totals. + """ categoryPercentages: CategoryPercentages - # Category totals for quick viewing. + """ + Category totals for quick viewing. + """ categoryTotals: CategoryTotals - # Various senders for each category. + """ + Various senders for each category. + """ detailTables: DetailTables } - # Domain object containing information for a given domain. + """ + Domain object containing information for a given domain. + """ type Domain implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Domain that scans will be ran on. + """ + Domain that scans will be ran on. + """ domain: DomainScalar - # The current dmarc phase the domain is compliant to. + """ + The current dmarc phase the domain is compliant to. + """ dmarcPhase: String - # Whether or not the domain has a aggregate dmarc report. + """ + Whether or not the domain has a aggregate dmarc report. + """ hasDMARCReport: Boolean - # The last time that a scan was ran on this domain. + """ + The last time that a scan was ran on this domain. + """ lastRan: String - # The status code when performing a DNS lookup for this domain. + """ + The status code when performing a DNS lookup for this domain. + """ rcode: String - # Domain Keys Identified Mail (DKIM) selector strings associated with domain. + """ + Domain Keys Identified Mail (DKIM) selector strings associated with domain. + """ selectors: [Selector] - # The domains scan status, based on the latest scan data. + """ + The domains scan status, based on the latest scan data. + """ status: DomainStatus - # Value that determines if a domain is excluded from any results and scans. + """ + Value that determines if a domain is excluded from any results and scans. + """ archived: Boolean - # Value that determines if a domain is possibly blocked. + """ + Value that determines if a domain is possibly blocked. + """ blocked: Boolean - # Value that determines if a domain has a wildcard sibling. + """ + Value that determines if a domain has a wildcard sibling. + """ wildcardSibling: Boolean - # Value that determines if a domain has a web scan pending. + """ + Value that determines if a domain has a web scan pending. + """ webScanPending: Boolean - # The organization that this domain belongs to. + """ + The organization that this domain belongs to. + """ organizations( - # Ordering options for organization connections + """ + Ordering options for organization connections + """ orderBy: OrganizationOrder - # String argument used to search for organizations. + """ + String argument used to search for organizations. + """ search: String - # Filter orgs based off of the user being an admin of them. + """ + Filter orgs based off of the user being an admin of them. + """ isAdmin: Boolean - # Filter org list to either include or exclude the super admin org. + """ + Filter org list to either include or exclude the super admin org. + """ includeSuperAdminOrg: Boolean - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): OrganizationConnection - # DNS scan results. + """ + DNS scan results. + """ dnsScan( - # Start date for date filter. + """ + Start date for date filter. + """ startDate: DateTime - # End date for date filter. + """ + End date for date filter. + """ endDate: DateTime - # Ordering options for DNS connections. + """ + Ordering options for DNS connections. + """ orderBy: DNSOrder - # Number of DNS scans to retrieve. + """ + Number of DNS scans to retrieve. + """ limit: Int - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): DNSScanConnection - # List of MX record diffs for a given domain. + """ + List of MX record diffs for a given domain. + """ mxRecordDiff( - # Start date for date filter. + """ + Start date for date filter. + """ startDate: DateTime - # End date for date filter. + """ + End date for date filter. + """ endDate: DateTime - # Ordering options for MX connections. + """ + Ordering options for MX connections. + """ orderBy: DNSOrder - # Number of MX scans to retrieve. + """ + Number of MX scans to retrieve. + """ limit: Int - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): MXRecordDiffConnection - # HTTPS, and TLS scan results. + """ + HTTPS, and TLS scan results. + """ web( - # Start date for date filter. + """ + Start date for date filter. + """ startDate: DateTime - # End date for date filter. + """ + End date for date filter. + """ endDate: DateTime - # Ordering options for web connections. + """ + Ordering options for web connections. + """ orderBy: WebOrder - # Number of web scans to retrieve. + """ + Number of web scans to retrieve. + """ limit: Int - # Exclude web scans which have pending status. + """ + Exclude web scans which have pending status. + """ excludePending: Boolean - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): WebConnection - # Summarized DMARC aggregate reports. + """ + Summarized DMARC aggregate reports. + """ dmarcSummaryByPeriod( - # The month in which the returned data is relevant to. + """ + The month in which the returned data is relevant to. + """ month: PeriodEnums! - # The year in which the returned data is relevant to. + """ + The year in which the returned data is relevant to. + """ year: Year! ): DmarcSummary - # Yearly summarized DMARC aggregate reports. + """ + Yearly summarized DMARC aggregate reports. + """ yearlyDmarcSummaries: [DmarcSummary] - # List of labelled tags users of an organization have applied to the claimed domain. + """ + List of labelled tags users of an organization have applied to the claimed domain. + """ claimTags: [String] - # Value that determines if a domain is excluded from an organization's results. + """ + Value that determines if a domain is excluded from an organization's results. + """ hidden: Boolean - # Value that determines if a user is affiliated with a domain, whether through organization affiliation, verified organization network affiliation, or through super admin status. + """ + Value that determines if a user is affiliated with a domain, whether through organization affiliation, verified organization network affiliation, or through super admin status. + """ userHasPermission: Boolean } - # String that conforms to a domain structure. + """ + String that conforms to a domain structure. + """ scalar DomainScalar - # A field that conforms to a DKIM selector. Must be either a single asterisk or a string where only alphanumeric characters and periods are allowed, string must also start and end with alphanumeric characters + """ + A field that conforms to a DKIM selector + """ scalar Selector - # This object contains how the domain is doing on the various scans we preform, based on the latest scan data. + """ + This object contains how the domain is doing on the various scans we preform, based on the latest scan data. + """ type DomainStatus { - # Certificates Status + """ + Certificates Status + """ certificates: StatusEnum - # Ciphers Status + """ + Ciphers Status + """ ciphers: StatusEnum - # Curves Status + """ + Curves Status + """ curves: StatusEnum - # DKIM Status + """ + DKIM Status + """ dkim: StatusEnum - # DMARC Status + """ + DMARC Status + """ dmarc: StatusEnum - # HTTPS Status + """ + HTTPS Status + """ https: StatusEnum - # HSTS Status + """ + HSTS Status + """ hsts: StatusEnum - # Policy Status + """ + Policy Status + """ policy: StatusEnum - # Protocols Status + """ + Protocols Status + """ protocols: StatusEnum - # SPF Status + """ + SPF Status + """ spf: StatusEnum - # SSL Status + """ + SSL Status + """ ssl: StatusEnum } - # Enum used to inform front end if there are any issues, info, or the domain passes a given check. + """ + Enum used to inform front end if there are any issues, info, or the domain passes a given check. + """ enum StatusEnum { - # If the given check meets the passing requirements. + """ + If the given check meets the passing requirements. + """ PASS - # If the given check has flagged something that can provide information on the domain that aren't scan related. + """ + If the given check has flagged something that can provide information on the domain that aren't scan related. + """ INFO - # If the given check does not meet the passing requirements + """ + If the given check does not meet the passing requirements + """ FAIL } - # A connection to a list of items. + """ + A connection to a list of items. + """ type OrganizationConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [OrganizationEdge] - # The total amount of organizations the user has access to. + """ + The total amount of organizations the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type OrganizationEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: Organization - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Organization object containing information for a given Organization. + """ + Organization object containing information for a given Organization. + """ type Organization implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # The organizations acronym. + """ + The organizations acronym. + """ acronym: Acronym - # The full name of the organization. + """ + The full name of the organization. + """ name: String - # Slugified name of the organization. + """ + Slugified name of the organization. + """ slug: Slug - # The zone which the organization belongs to. + """ + The zone which the organization belongs to. + """ zone: String - # The sector which the organization belongs to. + """ + The sector which the organization belongs to. + """ sector: String - # The country in which the organization resides. + """ + The country in which the organization resides. + """ country: String - # The province in which the organization resides. + """ + The province in which the organization resides. + """ province: String - # The city in which the organization resides. + """ + The city in which the organization resides. + """ city: String - # Whether the organization is a verified organization. + """ + Whether the organization is a verified organization. + """ verified: Boolean - # Summaries based on scan types that are preformed on the given organizations domains. + """ + Whether the organization is externally managed. + """ + externallyManaged: Boolean + + """ + Summaries based on scan types that are preformed on the given organizations domains. + """ summaries: OrganizationSummary - # The number of domains associated with this organization. + """ + Historical summaries based on scan types that are preformed on the given organizations domains. + """ + historicalSummaries( + """ + The month in which the returned data is relevant to. + """ + month: PeriodEnums! + + """ + The year in which the returned data is relevant to. + """ + year: Year! + + """ + The direction in which to sort the data. + """ + sortDirection: OrderDirection! + ): OrganizationSummaryConnection + + """ + The number of domains associated with this organization. + """ domainCount: Int - # CSV formatted output of all domains in the organization including their email and web scan statuses. + """ + CSV formatted output of all domains in the organization including their email and web scan statuses. + """ toCsv( - # Filters used to limit domains returned. + """ + Filters used to limit domains returned. + """ filters: [DomainFilter] ): String - # The domains which are associated with this organization. + """ + The domains which are associated with this organization. + """ domains( - # Ordering options for domain connections. + """ + Ordering options for domain connections. + """ orderBy: DomainOrder - # Limit domains to those that belong to an organization that has ownership. + """ + Limit domains to those that belong to an organization that has ownership. + """ ownership: Boolean - # String used to search for domains. + """ + String used to search for domains. + """ search: String - # Filters used to limit domains returned. + """ + Filters used to limit domains returned. + """ filters: [DomainFilter] - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): DomainConnection - # Organization affiliations to various users. + """ + Organization affiliations to various users. + """ affiliations( - # Ordering options for affiliation connections. + """ + Ordering options for affiliation connections. + """ orderBy: AffiliationUserOrder - # String used to search for affiliated users. + """ + String used to search for affiliated users. + """ search: String - # Exclude (false) or include only (true) pending affiliations in the results. + """ + Exclude (false) or include only (true) pending affiliations in the results. + """ includePending: Boolean - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): AffiliationConnection - # Value that determines if a user is affiliated with an organization, whether through organization affiliation, verified affiliation, or through super admin status. + """ + Value that determines if a user is affiliated with an organization, whether through organization affiliation, verified affiliation, or through super admin status. + """ userHasPermission: Boolean } - # A field whose value consists of upper case or lower case letters or underscores with a length between 1 and 50. + """ + A field whose value consists of upper case or lower case letters or underscores with a length between 1 and 50. + """ scalar Acronym - # A field whose values contain numbers, letters, dashes, and underscores. + """ + A field whose values contain numbers, letters, dashes, and underscores. + """ scalar Slug - # Summaries based on domains that the organization has claimed. + """ + Summaries based on domains that the organization has claimed. + """ type OrganizationSummary { - # Summary based on DMARC scan results for a given organization. + """ + Date that the summary was computed. + """ + date: Date + + """ + Summary based on DMARC scan results for a given organization. + """ dmarc: CategorizedSummary - # Summary based on HTTPS scan results for a given organization. + """ + Summary based on HTTPS scan results for a given organization. + """ https: CategorizedSummary - # Summary based on mail scan results for a given organization. + """ + Summary based on mail scan results for a given organization. + """ mail: CategorizedSummary - # Summary based on web scan results for a given organization. + """ + Summary based on web scan results for a given organization. + """ web: CategorizedSummary - # Summary based on DMARC phases for a given organization. + """ + Summary based on DMARC phases for a given organization. + """ dmarcPhase: CategorizedSummary - # Summary based on SSL scan results for a given organization. + """ + Summary based on SSL scan results for a given organization. + """ ssl: CategorizedSummary - # Summary based on HTTPS and HSTS scan results for a given organization. + """ + Summary based on HTTPS and HSTS scan results for a given organization. + """ webConnections: CategorizedSummary - # Summary based on SPF scan results for a given organization. + """ + Summary based on SPF scan results for a given organization. + """ spf: CategorizedSummary - # Summary based on DKIM scan results for a given organization. + """ + Summary based on DKIM scan results for a given organization. + """ dkim: CategorizedSummary - - # Summary based on HTTPS scan results for a given organization that includes domains marked as hidden. - httpsIncludeHidden: CategorizedSummary - - # Summary based on HTTPS scan results for a given organization that includes domains marked as hidden. - dmarcIncludeHidden: CategorizedSummary } - # This object contains the list of different categories for pre-computed - # summary data with the computed total for how many domains in total are - # being compared. + """ + A date string, such as 2007-12-03, compliant with the 'full-date' format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. + """ + scalar Date + + """ + This object contains the list of different categories for pre-computed + summary data with the computed total for how many domains in total are + being compared. + """ type CategorizedSummary { - # List of SummaryCategory objects with data for different computed categories. + """ + List of SummaryCategory objects with data for different computed categories. + """ categories: [SummaryCategory] - # Total domains that were check under this summary. + """ + Total domains that were check under this summary. + """ total: Int } - # This object contains the information for each type of summary that has been pre-computed + """ + This object contains the information for each type of summary that has been pre-computed + """ type SummaryCategory { - # Category of computed summary which the other fields relate to. + """ + Category of computed summary which the other fields relate to. + """ name: String - # Total count of domains that fall into this category. + """ + Total count of domains that fall into this category. + """ count: Int - # Percentage compared to other categories. + """ + Percentage compared to other categories. + """ percentage: Float } - # This object is used to provide filtering options when querying org-claimed domains. + """ + A connection to a list of items. + """ + type OrganizationSummaryConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + + """ + A list of edges. + """ + edges: [OrganizationSummaryEdge] + + """ + The total amount of dmarc summaries the user has access to. + """ + totalCount: Int + } + + """ + An edge in a connection. + """ + type OrganizationSummaryEdge { + """ + The item at the end of the edge + """ + node: OrganizationSummary + + """ + A cursor for use in pagination + """ + cursor: String! + } + + """ + An enum used to select information from the dmarc-report-api. + """ + enum PeriodEnums { + """ + The month of January. + """ + JANUARY + + """ + The month of February. + """ + FEBRUARY + + """ + The month of March. + """ + MARCH + + """ + The month of April. + """ + APRIL + + """ + The month of May. + """ + MAY + + """ + The month of June. + """ + JUNE + + """ + The month of July. + """ + JULY + + """ + The month of August. + """ + AUGUST + + """ + The month of September. + """ + SEPTEMBER + + """ + The month of October. + """ + OCTOBER + + """ + The month of November. + """ + NOVEMBER + + """ + The month of December. + """ + DECEMBER + + """ + The last 30 days. + """ + LAST30DAYS + + """ + The last year. + """ + LASTYEAR + + """ + The year to date. + """ + YTD + } + + """ + A field that conforms to a 4 digit integer. + """ + scalar Year + + """ + This object is used to provide filtering options when querying org-claimed domains. + """ input DomainFilter { - # Category of filter to be applied. + """ + Category of filter to be applied. + """ filterCategory: DomainOrderField - # First value equals or does not equal second value. + """ + First value equals or does not equal second value. + """ comparison: ComparisonEnums - # Status type or tag label. + """ + Status type or tag label. + """ filterValue: filterValueEnums } - # Properties by which domain connections can be ordered. + """ + Properties by which domain connections can be ordered. + """ enum DomainOrderField { - # Order domains by certificates status. + """ + Order domains by certificates status. + """ CERTIFICATES_STATUS - # Order domains by ciphers status. + """ + Order domains by ciphers status. + """ CIPHERS_STATUS - # Order domains by curves status. + """ + Order domains by curves status. + """ CURVES_STATUS - # Order domains by domain. + """ + Order domains by domain. + """ DOMAIN - # Order domains by dkim status. + """ + Order domains by dkim status. + """ DKIM_STATUS - # Order domains by dmarc status. + """ + Order domains by dmarc status. + """ DMARC_STATUS - # Order domains by https status. + """ + Order domains by https status. + """ HTTPS_STATUS - # Order domains by hsts status. + """ + Order domains by hsts status. + """ HSTS_STATUS - # Order domains by ITPIN policy status. + """ + Order domains by ITPIN policy status. + """ POLICY_STATUS - # Order domains by protocols status. + """ + Order domains by protocols status. + """ PROTOCOLS_STATUS - # Order domains by spf status. + """ + Order domains by spf status. + """ SPF_STATUS - # Order domains by tags. + """ + Order domains by tags. + """ TAGS } - # + """ + """ enum ComparisonEnums { - # + """ + """ EQUAL - # + """ + """ NOT_EQUAL } - # + """ + """ enum filterValueEnums { - # If the given check meets the passing requirements. + """ + If the given check meets the passing requirements. + """ PASS - # If the given check has flagged something that can provide information on the domain that aren't scan related. + """ + If the given check has flagged something that can provide information on the domain that aren't scan related. + """ INFO - # If the given check does not meet the passing requirements + """ + If the given check does not meet the passing requirements + """ FAIL - # English label for tagging domains as new to the system. + """ + English label for tagging domains as new to the system. + """ NEW - # French label for tagging domains as new to the system. + """ + French label for tagging domains as new to the system. + """ NOUVEAU - # Bilingual Label for tagging domains as a production environment. + """ + Bilingual Label for tagging domains as a production environment. + """ PROD - # English label for tagging domains as a staging environment. + """ + English label for tagging domains as a staging environment. + """ STAGING - # French label for tagging domains as a staging environment. + """ + French label for tagging domains as a staging environment. + """ DEV - # Bilingual label for tagging domains as a test environment. + """ + Bilingual label for tagging domains as a test environment. + """ TEST - # Bilingual label for tagging domains as web-hosting. + """ + Bilingual label for tagging domains as web-hosting. + """ WEB - # English label for tagging domains that are not active. + """ + English label for tagging domains that are not active. + """ INACTIVE - # French label for tagging domains that are not active. + """ + French label for tagging domains that are not active. + """ INACTIF - # English label for tagging domains that are hidden. + """ + English label for tagging domains that are hidden. + """ HIDDEN - # English label for tagging domains that are archived. + """ + English label for tagging domains that are archived. + """ ARCHIVED - # Label for tagging domains that have an rcode status of NXDOMAIN. + """ + Label for tagging domains that have an rcode status of NXDOMAIN. + """ NXDOMAIN - # Label for tagging domains that are possibly blocked by a firewall. + """ + Label for tagging domains that are possibly blocked by a firewall. + """ BLOCKED - # Label for tagging domains that have a wildcard sibling. + """ + Label for tagging domains that have a wildcard sibling. + """ WILDCARD_SIBLING - # Label for tagging domains that have a pending web scan. + """ + Label for tagging domains that have a pending web scan. + """ SCAN_PENDING - # English label for tagging domains that are outside the scope of the project. + """ + English label for tagging domains that are outside the scope of the project. + """ OUTSIDE - # French label for tagging domains that are outside the scope of the project. + """ + French label for tagging domains that are outside the scope of the project. + """ EXTERIEUR } - # A connection to a list of items. + """ + A connection to a list of items. + """ type DomainConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [DomainEdge] - # The total amount of domains the user has access to. + """ + The total amount of domains the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type DomainEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: Domain - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Ordering options for domain connections. + """ + Ordering options for domain connections. + """ input DomainOrder { - # The field to order domains by. + """ + The field to order domains by. + """ field: DomainOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # A connection to a list of items. + """ + A connection to a list of items. + """ type AffiliationConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [AffiliationEdge] - # The total amount of affiliations the user has access to. + """ + The total amount of affiliations the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type AffiliationEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: Affiliation - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # User Affiliations containing the permission level for the given organization, the users information, and the organizations information. + """ + User Affiliations containing the permission level for the given organization, the users information, and the organizations information. + """ type Affiliation implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # User's level of access to a given organization. + """ + User's level of access to a given organization. + """ permission: RoleEnums - # The affiliated users information. + """ + The affiliated users information. + """ user: SharedUser - # The affiliated organizations information. + """ + The affiliated organizations information. + """ organization: Organization } - # This object is used for showing none personal user details, - # and is used for limiting admins to the personal details of users. + """ + This object is used for showing none personal user details, + and is used for limiting admins to the personal details of users. + """ type SharedUser implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Users display name. + """ + Users display name. + """ displayName: String - # Users email address. + """ + Users email address. + """ userName: EmailAddress - # Has the user email verified their account. + """ + Has the user email verified their account. + """ emailValidated: Boolean - # Does the user want to see new features in progress. + """ + Does the user want to see new features in progress. + """ insideUser: Boolean - # Users affiliations to various organizations. + """ + Users affiliations to various organizations. + """ affiliations( - # Ordering options for affiliation connections. + """ + Ordering options for affiliation connections. + """ orderBy: AffiliationOrgOrder - # String used to search for affiliated organizations. + """ + String used to search for affiliated organizations. + """ search: String - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): AffiliationConnection } - # Ordering options for affiliation connections. + """ + Ordering options for affiliation connections. + """ input AffiliationOrgOrder { - # The field to order affiliations by. + """ + The field to order affiliations by. + """ field: AffiliationOrgOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which affiliation connections can be ordered. + """ + Properties by which affiliation connections can be ordered. + """ enum AffiliationOrgOrderField { - # Order affiliations by org acronym. + """ + Order affiliations by org acronym. + """ ORG_ACRONYM - # Order affiliations by org name. + """ + Order affiliations by org name. + """ ORG_NAME - # Order affiliations by org slug. + """ + Order affiliations by org slug. + """ ORG_SLUG - # Order affiliations by org zone. + """ + Order affiliations by org zone. + """ ORG_ZONE - # Order affiliations by org sector. + """ + Order affiliations by org sector. + """ ORG_SECTOR - # Order affiliations by org country. + """ + Order affiliations by org country. + """ ORG_COUNTRY - # Order affiliations by org province. + """ + Order affiliations by org province. + """ ORG_PROVINCE - # Order affiliations by org city. + """ + Order affiliations by org city. + """ ORG_CITY - # Order affiliations by org verification. + """ + Order affiliations by org verification. + """ ORG_VERIFIED - # Order affiliations by org summary mail pass count. + """ + Order affiliations by org summary mail pass count. + """ ORG_SUMMARY_MAIL_PASS - # Order affiliations by org summary mail fail count. + """ + Order affiliations by org summary mail fail count. + """ ORG_SUMMARY_MAIL_FAIL - # Order affiliations by org summary mail total count. + """ + Order affiliations by org summary mail total count. + """ ORG_SUMMARY_MAIL_TOTAL - # Order affiliations by org summary web pass count. + """ + Order affiliations by org summary web pass count. + """ ORG_SUMMARY_WEB_PASS - # Order affiliations by org summary web fail count. + """ + Order affiliations by org summary web fail count. + """ ORG_SUMMARY_WEB_FAIL - # Order affiliations by org summary web total count. + """ + Order affiliations by org summary web total count. + """ ORG_SUMMARY_WEB_TOTAL - # Order affiliations by org domain count. + """ + Order affiliations by org domain count. + """ ORG_DOMAIN_COUNT } - # Ordering options for affiliation connections. + """ + Ordering options for affiliation connections. + """ input AffiliationUserOrder { - # The field to order affiliations by. + """ + The field to order affiliations by. + """ field: AffiliationUserOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which affiliation connections can be ordered. + """ + Properties by which affiliation connections can be ordered. + """ enum AffiliationUserOrderField { - # Order affiliation edges by username. - USER_USERNAME - - # Order affiliation edges by displayName. - USER_DISPLAYNAME + """ + Order affiliations by username. + """ + USERNAME - # Order affiliation edges by user verification status. - USER_EMAIL_VALIDATED + """ + Order affiliations by display name. + """ + DISPLAY_NAME - # Order affiliation edges by user insider status. - USER_INSIDER - - # Order affiliation edges by amount of total affiliations. - USER_AFFILIATIONS_COUNT + """ + Order affiliations by permission. + """ + PERMISSION } - # Ordering options for organization connections + """ + Ordering options for organization connections + """ input OrganizationOrder { - # The field to order organizations by. + """ + The field to order organizations by. + """ field: OrganizationOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which organization connections can be ordered. + """ + Properties by which organization connections can be ordered. + """ enum OrganizationOrderField { - # Order organizations by acronym. + """ + Order organizations by acronym. + """ ACRONYM - # Order organizations by name. + """ + Order organizations by name. + """ NAME - # Order organizations by slug. + """ + Order organizations by slug. + """ SLUG - # Order organizations by zone. + """ + Order organizations by zone. + """ ZONE - # Order organizations by sector. + """ + Order organizations by sector. + """ SECTOR - # Order organizations by country. + """ + Order organizations by country. + """ COUNTRY - # Order organizations by province. + """ + Order organizations by province. + """ PROVINCE - # Order organizations by city. + """ + Order organizations by city. + """ CITY - # Order organizations by verified. + """ + Order organizations by verified. + """ VERIFIED - # Order organizations by summary mail pass count. + """ + Order organizations by summary mail pass count. + """ SUMMARY_MAIL_PASS - # Order organizations by summary mail fail count. + """ + Order organizations by summary mail fail count. + """ SUMMARY_MAIL_FAIL - # Order organizations by summary mail total count. + """ + Order organizations by summary mail total count. + """ SUMMARY_MAIL_TOTAL - # Order organizations by summary web pass count. + """ + Order organizations by summary web pass count. + """ SUMMARY_WEB_PASS - # Order organizations by summary web fail count. + """ + Order organizations by summary web fail count. + """ SUMMARY_WEB_FAIL - # Order organizations by summary web total count. + """ + Order organizations by summary web total count. + """ SUMMARY_WEB_TOTAL - # Order organizations by domain count. + """ + Order organizations by domain count. + """ DOMAIN_COUNT } - # A connection to a list of items. + """ + A connection to a list of items. + """ type DNSScanConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [DNSScanEdge] - # The total amount of DNS scans related to a given domain. + """ + The total amount of DNS scans related to a given domain. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type DNSScanEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: DNSScan - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Results of DKIM, DMARC, and SPF scans on the given domain. + """ + Results of DKIM, DMARC, and SPF scans on the given domain. + """ type DNSScan implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # The domain the scan was ran on. + """ + The domain the scan was ran on. + """ domain: String - # The time when the scan was initiated. + """ + The time when the scan was initiated. + """ timestamp: DateTime - # String of the base domain the scan was run on. + """ + String of the base domain the scan was run on. + """ baseDomain: String - # Whether or not there are DNS records for the domain scanned. + """ + Whether or not there are DNS records for the domain scanned. + """ recordExists: Boolean - # The chain CNAME/IP addresses for the domain. + """ + The chain CNAME/IP addresses for the domain. + """ resolveChain: [[String]] - # The CNAME for the domain (if it exists). + """ + The CNAME for the domain (if it exists). + """ cnameRecord: String - # The MX records for the domain (if they exist). + """ + The MX records for the domain (if they exist). + """ mxRecords: MXRecord - # The NS records for the domain. + """ + The NS records for the domain. + """ nsRecords: NSRecord - # The DMARC scan results for the domain. + """ + The DMARC scan results for the domain. + """ dmarc: DMARC - # The SPF scan results for the domain. + """ + The SPF scan results for the domain. + """ spf: SPF - # The SKIM scan results for the domain. + """ + The SKIM scan results for the domain. + """ dkim: DKIM } type MXRecord { - # Hosts listed in the domain's MX record. + """ + Hosts listed in the domain's MX record. + """ hosts: [MXHost] - # Additional warning info about the MX record. + """ + Additional warning info about the MX record. + """ warnings: [String] + + """ + Error message if the MX record could not be retrieved. + """ + error: String } - # Hosts listed in the domain's MX record. + """ + Hosts listed in the domain's MX record. + """ type MXHost { - # The preference (or priority) of the host. + """ + The preference (or priority) of the host. + """ preference: Int - # The hostname of the given host. + """ + The hostname of the given host. + """ hostname: String - # The IP addresses for the given host. + """ + The IP addresses for the given host. + """ addresses: [String] } type NSRecord { - # Hostnames for the nameservers for the domain. + """ + Hostnames for the nameservers for the domain. + """ hostnames: [String] - # Additional warning info about the NS record. + """ + Additional warning info about the NS record. + """ warnings: [String] + + """ + Error message if the NS record could not be retrieved. + """ + error: String } - # Domain-based Message Authentication, Reporting, and Conformance - # (DMARC) is a scalable mechanism by which a mail-originating - # organization can express domain-level policies and preferences for - # message validation, disposition, and reporting, that a mail-receiving - # organization can use to improve mail handling. + """ + Domain-based Message Authentication, Reporting, and Conformance + (DMARC) is a scalable mechanism by which a mail-originating + organization can express domain-level policies and preferences for + message validation, disposition, and reporting, that a mail-receiving + organization can use to improve mail handling. + """ type DMARC { - # The compliance status for DMARC for the scanned domain. + """ + The compliance status for DMARC for the scanned domain. + """ status: String - # DMARC record retrieved during scan. + """ + DMARC record retrieved during scan. + """ record: String - # The requested policy you wish mailbox providers to apply - # when your email fails DMARC authentication and alignment checks. + """ + The requested policy you wish mailbox providers to apply + when your email fails DMARC authentication and alignment checks. + """ pPolicy: String - # This tag is used to indicate a requested policy for all - # subdomains where mail is failing the DMARC authentication and alignment checks. + """ + This tag is used to indicate a requested policy for all + subdomains where mail is failing the DMARC authentication and alignment checks. + """ spPolicy: String - # The percentage of messages to which the DMARC policy is to be applied. + """ + The percentage of messages to which the DMARC policy is to be applied. + """ pct: Int - # The current phase of the DMARC implementation. + """ + The current phase of the DMARC implementation. + """ phase: String - # List of positive tags for the scanned domain from this scan. + """ + List of positive tags for the scanned domain from this scan. + """ positiveTags: [GuidanceTag] - # List of neutral tags for the scanned domain from this scan. + """ + List of neutral tags for the scanned domain from this scan. + """ neutralTags: [GuidanceTag] - # List of negative tags for the scanned domain from this scan. + """ + List of negative tags for the scanned domain from this scan. + """ negativeTags: [GuidanceTag] } - # Details for a given guidance tag based on https://github.com/canada-ca/tracker/wiki/Guidance-Tags + """ + Details for a given guidance tag based on https://github.com/canada-ca/tracker/wiki/Guidance-Tags + """ type GuidanceTag implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # The guidance tag ID. + """ + The guidance tag ID. + """ tagId: String - # The guidance tag name. + """ + The guidance tag name. + """ tagName: String - # Guidance for changes to record, or to maintain current stance. + """ + Guidance for changes to record, or to maintain current stance. + """ guidance: String - # Links to implementation guidance for a given tag. + """ + Links to implementation guidance for a given tag. + """ refLinks: [RefLinks] - # Links to technical information for a given tag. + """ + Links to technical information for a given tag. + """ refLinksTech: [RefLinks] } - # Object containing the information of various links for guidance or technical documentation. + """ + Object containing the information of various links for guidance or technical documentation. + """ type RefLinks { - # Title of the guidance link. + """ + Title of the guidance link. + """ description: String - # URL for the guidance documentation. + """ + URL for the guidance documentation. + """ refLink: String } - # Email on the Internet can be forged in a number of ways. In - # particular, existing protocols place no restriction on what a sending - # host can use as the "MAIL FROM" of a message or the domain given on - # the SMTP HELO/EHLO commands. Version 1 of the Sender Policy Framework (SPF) - # protocol is where Administrative Management Domains (ADMDs) can explicitly - # authorize the hosts that are allowed to use their domain names, and a - # receiving host can check such authorization. + """ + Email on the Internet can be forged in a number of ways. In + particular, existing protocols place no restriction on what a sending + host can use as the "MAIL FROM" of a message or the domain given on + the SMTP HELO/EHLO commands. Version 1 of the Sender Policy Framework (SPF) + protocol is where Administrative Management Domains (ADMDs) can explicitly + authorize the hosts that are allowed to use their domain names, and a + receiving host can check such authorization. + """ type SPF { - # The compliance status for SPF for the scanned domain. + """ + The compliance status for SPF for the scanned domain. + """ status: String - # SPF record retrieved during the scan of the given domain. + """ + SPF record retrieved during the scan of the given domain. + """ record: String - # The amount of DNS lookups. + """ + The amount of DNS lookups. + """ lookups: Int - # Instruction of what a recipient should do if there is not a match to your SPF record. + """ + Instruction of what a recipient should do if there is not a match to your SPF record. + """ spfDefault: String - # List of positive tags for the scanned domain from this scan. + """ + List of positive tags for the scanned domain from this scan. + """ positiveTags: [GuidanceTag] - # List of neutral tags for the scanned domain from this scan. + """ + List of neutral tags for the scanned domain from this scan. + """ neutralTags: [GuidanceTag] - # List of negative tags for the scanned domain from this scan. + """ + List of negative tags for the scanned domain from this scan. + """ negativeTags: [GuidanceTag] } - # DomainKeys Identified Mail (DKIM) permits a person, role, or - # organization that owns the signing domain to claim some - # responsibility for a message by associating the domain with the - # message. This can be an author's organization, an operational relay, - # or one of their agents. + """ + DomainKeys Identified Mail (DKIM) permits a person, role, or + organization that owns the signing domain to claim some + responsibility for a message by associating the domain with the + message. This can be an author's organization, an operational relay, + or one of their agents. + """ type DKIM { - # The compliance status for DKIM for the scanned domain. + """ + The compliance status for DKIM for the scanned domain. + """ status: String - # List of positive tags for the scanned domain from this scan. + """ + List of positive tags for the scanned domain from this scan. + """ positiveTags: [GuidanceTag] - # List of neutral tags for the scanned domain from this scan. + """ + List of neutral tags for the scanned domain from this scan. + """ neutralTags: [GuidanceTag] - # List of negative tags for the scanned domain from this scan. + """ + List of negative tags for the scanned domain from this scan. + """ negativeTags: [GuidanceTag] - # Individual scans results for each DKIM selector. + """ + Individual scans results for each DKIM selector. + """ selectors: [DKIMSelectorResult] } - # DomainKeys Identified Mail (DKIM) permits a person, role, or - # organization that owns the signing domain to claim some - # responsibility for a message by associating the domain with the - # message. This can be an author's organization, an operational relay, - # or one of their agents. + """ + DomainKeys Identified Mail (DKIM) permits a person, role, or + organization that owns the signing domain to claim some + responsibility for a message by associating the domain with the + message. This can be an author's organization, an operational relay, + or one of their agents. + """ type DKIMSelectorResult { - # The selector which was scanned. + """ + The selector which was scanned. + """ selector: String - # The compliance status for DKIM for the scanned domain. + """ + The compliance status for DKIM for the scanned domain. + """ status: String - # DKIM record retrieved during scan. + """ + DKIM record retrieved during scan. + """ record: String - # Size of the Public Key in bits. + """ + Size of the Public Key in bits. + """ keyLength: String - # Type of DKIM key used. + """ + Type of DKIM key used. + """ keyType: String - # The public exponent used for DKIM. + """ + The public exponent used for DKIM. + """ publicExponent: Int - # The key modulus used. + """ + The key modulus used. + """ keyModulus: String - # List of positive tags for the scanned domain from this scan. + """ + List of positive tags for the scanned domain from this scan. + """ positiveTags: [GuidanceTag] - # List of neutral tags for the scanned domain from this scan. + """ + List of neutral tags for the scanned domain from this scan. + """ neutralTags: [GuidanceTag] - # List of negative tags for the scanned domain from this scan. + """ + List of negative tags for the scanned domain from this scan. + """ negativeTags: [GuidanceTag] } - # Ordering options for DNS connections. + """ + Ordering options for DNS connections. + """ input DNSOrder { - # The field to order DNS scans by. + """ + The field to order DNS scans by. + """ field: DNSOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which DNS connections can be ordered. + """ + Properties by which DNS connections can be ordered. + """ enum DNSOrderField { - # Order DNS edges by timestamp. + """ + Order DNS edges by timestamp. + """ TIMESTAMP } - # A connection to a list of items. + """ + A connection to a list of items. + """ type MXRecordDiffConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [MXRecordDiffEdge] - # The total amount of DNS scans related to a given domain. + """ + The total amount of DNS scans related to a given domain. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type MXRecordDiffEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: MXRecordDiff - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } type MXRecordDiff { - # The ID of an object + """ + The ID of an object + """ id: ID! - # The time when the scan was initiated. + """ + The time when the scan was initiated. + """ timestamp: DateTime - # The MX records for the domain (if they exist). + """ + The MX records for the domain (if they exist). + """ mxRecords: MXRecord } - # A connection to a list of items. + """ + A connection to a list of items. + """ type WebConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [WebEdge] - # The total amount of web scans related to a given domain. + """ + The total amount of web scans related to a given domain. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type WebEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: Web - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Results of TLS and HTTP connection scans on the given domain. + """ + Results of TLS and HTTP connection scans on the given domain. + """ type Web implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # The domain string the scan was ran on. + """ + The domain string the scan was ran on. + """ domain: String - # The time when the scan was initiated. + """ + The time when the scan was initiated. + """ timestamp: DateTime - # Results of the web scan at each IP address. + """ + Results of the web scan at each IP address. + """ results: [WebScan] } - # Information for the TLS and HTTP connection scans on the given domain. + """ + Information for the TLS and HTTP connection scans on the given domain. + """ type WebScan { - # IP address for scan target. + """ + IP address for scan target. + """ ipAddress: String - # The status of the scan for the given domain and IP address. + """ + The status of the scan for the given domain and IP address. + """ status: String - # Results of TLS and HTTP connection scans on the given domain. + """ + Results of TLS and HTTP connection scans on the given domain. + """ results: WebScanResult } - # Results of TLS and HTTP connection scans on the given domain. + """ + Results of TLS and HTTP connection scans on the given domain. + """ type WebScanResult { - # The time when the scan was initiated. + """ + The time when the scan was initiated. + """ timestamp: DateTime - # The result for the TLS scan for the scanned server. + """ + The result for the TLS scan for the scanned server. + """ tlsResult: TLSResult - # The result for the HTTP connection scan for the scanned server. + """ + The result for the HTTP connection scan for the scanned server. + """ connectionResults: WebConnectionResult } - # Results of TLS scans on the given domain. + """ + Results of TLS scans on the given domain. + """ type TLSResult { - # The IP address of the domain scanned. + """ + The IP address of the domain scanned. + """ ipAddress: String - # Information regarding the server which was scanned. + """ + Information regarding the server which was scanned. + """ serverLocation: ServerLocation - # Information for the TLS certificate retrieved from the scanned server. + """ + Information for the TLS certificate retrieved from the scanned server. + """ certificateChainInfo: CertificateChainInfo - # Whether or not the scanned server supports ECDH key exchange. + """ + Whether or not the scanned server supports ECDH key exchange. + """ supportsEcdhKeyExchange: Boolean - # Whether or not the scanned server is vulnerable to heartbleed. + """ + Whether or not the scanned server is vulnerable to heartbleed. + """ heartbleedVulnerable: Boolean - # Whether or not the scanned server is vulnerable to heartbleed. + """ + Whether or not the scanned server is vulnerable to heartbleed. + """ robotVulnerable: String - # Whether or not the scanned server is vulnerable to CCS injection. + """ + Whether or not the scanned server is vulnerable to CCS injection. + """ ccsInjectionVulnerable: Boolean - # An object containing the various TLS protocols and which suites are enabled for each protocol. + """ + An object containing the various TLS protocols and which suites are enabled for each protocol. + """ acceptedCipherSuites: AcceptedCipherSuites - # List of the scanned servers accepted elliptic curves and their strength. + """ + List of the scanned servers accepted elliptic curves and their strength. + """ acceptedEllipticCurves: [EllipticCurve] - # List of positive tags for the scanned server from this scan. + """ + List of positive tags for the scanned server from this scan. + """ positiveTags: [GuidanceTag] - # List of neutral tags for the scanned server from this scan. + """ + List of neutral tags for the scanned server from this scan. + """ neutralTags: [GuidanceTag] - # List of negative tags for the scanned server from this scan. + """ + List of negative tags for the scanned server from this scan. + """ negativeTags: [GuidanceTag] - # The compliance status of the certificate bundle for the scanned server from this scan. + """ + The compliance status of the certificate bundle for the scanned server from this scan. + """ certificateStatus: String - # The compliance status for TLS for the scanned server from this scan. + """ + The compliance status for TLS for the scanned server from this scan. + """ sslStatus: String - # The compliance status for TLS protocol for the scanned server from this scan. + """ + The compliance status for TLS protocol for the scanned server from this scan. + """ protocolStatus: String - # The compliance status for cipher suites for the scanned server from this scan. + """ + The compliance status for cipher suites for the scanned server from this scan. + """ cipherStatus: String - # The compliance status for ECDH curves for the scanned server from this scan. + """ + The compliance status for ECDH curves for the scanned server from this scan. + """ curveStatus: String } type ServerLocation { - # Hostname which was scanned. + """ + Hostname which was scanned. + """ hostname: String - # IP address used for scan. + """ + IP address used for scan. + """ ipAddress: String } - # + """ + """ type CertificateChainInfo { - # Validation results from each trust store. + """ + Validation results from each trust store. + """ pathValidationResults: [PathValidationResults] - # True if domain is not listed on the given TLS certificate. + """ + True if domain is not listed on the given TLS certificate. + """ badHostname: Boolean - # Whether or not the TLS certificate includes the OCSP Must-Staple extension. + """ + Whether or not the TLS certificate includes the OCSP Must-Staple extension. + """ mustHaveStaple: Boolean - # Whether or not the leaf (server) certificate is an Extended Validation (EV) certificate. + """ + Whether or not the leaf (server) certificate is an Extended Validation (EV) certificate. + """ leafCertificateIsEv: Boolean - # Whether or not the certificate bundle includes the anchor (root) certificate. + """ + Whether or not the certificate bundle includes the anchor (root) certificate. + """ receivedChainContainsAnchorCertificate: Boolean - # Whether or not the certificates in the certificate bundles are in the correct order. + """ + Whether or not the certificates in the certificate bundles are in the correct order. + """ receivedChainHasValidOrder: Boolean - # Whether or not any certificates in the certificate bundle were signed using the SHA1 algorithm. + """ + Whether or not any certificates in the certificate bundle were signed using the SHA1 algorithm. + """ verifiedChainHasSha1Signature: Boolean - # Whether or not the certificate chain includes a distrusted Symantec certificate. + """ + Whether or not the certificate chain includes a distrusted Symantec certificate. + """ verifiedChainHasLegacySymantecAnchor: Boolean - # The certificate chain which was used to create the TLS connection. + """ + The certificate chain which was used to create the TLS connection. + """ certificateChain: [Certificate] - # Whether or not the certificate chain passed validation. + """ + Whether or not the certificate chain passed validation. + """ passedValidation: Boolean } - # Validation results from each trust store. + """ + Validation results from each trust store. + """ type PathValidationResults { - # Error string which occurred when attempting to validate certificate if error exists, else null. + """ + Error string which occurred when attempting to validate certificate if error exists, else null. + """ opensslErrorString: String - # Whether or not the certificate was successfully validated. + """ + Whether or not the certificate was successfully validated. + """ wasValidationSuccessful: Boolean - # Trust store used to validate TLS certificate. + """ + Trust store used to validate TLS certificate. + """ trustStore: TrustStore } - # Trust store used to validate TLS certificate. + """ + Trust store used to validate TLS certificate. + """ type TrustStore { - # Name of trust store used to validate certificate. + """ + Name of trust store used to validate certificate. + """ name: String - # Version of trust store used to validate certificate. + """ + Version of trust store used to validate certificate. + """ version: String } - # Certificate from the scanned server. + """ + Certificate from the scanned server. + """ type Certificate { - # The date which the certificate becomes initially becomes valid. + """ + The date which the certificate becomes initially becomes valid. + """ notValidBefore: String - # The date which the certificate becomes invalid. + """ + The date which the certificate becomes invalid. + """ notValidAfter: String - # The entity which signed the certificate. + """ + The entity which signed the certificate. + """ issuer: String - # The entity for which the certificate was created for. + """ + The entity for which the certificate was created for. + """ subject: String - # Whether or not the certificate is expired. + """ + Whether or not the certificate is expired. + """ expiredCert: Boolean - # Whether or not the certificate is self-signed. + """ + Whether or not the certificate is self-signed. + """ selfSignedCert: Boolean - # Whether or not the certificate has been revoked. + """ + Whether or not the certificate has been revoked. + """ certRevoked: Boolean - # The status of the certificate revocation check. + """ + The status of the certificate revocation check. + """ certRevokedStatus: String - # The list of common names for the given certificate. + """ + The list of common names for the given certificate. + """ commonNames: [String] - # The serial number for the given certificate. + """ + The serial number for the given certificate. + """ serialNumber: String - # The hashing algorithm used to validate this certificate. + """ + The hashing algorithm used to validate this certificate. + """ signatureHashAlgorithm: String - # The list of all alternative (domain)names which can use this certificate. + """ + The list of all alternative (domain)names which can use this certificate. + """ sanList: [String] } - # List of accepted cipher suites separated by TLS version. + """ + List of accepted cipher suites separated by TLS version. + """ type AcceptedCipherSuites { - # Accepted cipher suites for SSL2. + """ + Accepted cipher suites for SSL2. + """ ssl2_0CipherSuites: [CipherSuite] - # Accepted cipher suites for SSL3. + """ + Accepted cipher suites for SSL3. + """ ssl3_0CipherSuites: [CipherSuite] - # Accepted cipher suites for TLS1.0. + """ + Accepted cipher suites for TLS1.0. + """ tls1_0CipherSuites: [CipherSuite] - # Accepted cipher suites for TLS1.1. + """ + Accepted cipher suites for TLS1.1. + """ tls1_1CipherSuites: [CipherSuite] - # Accepted cipher suites for TLS1.2. + """ + Accepted cipher suites for TLS1.2. + """ tls1_2CipherSuites: [CipherSuite] - # Accepted cipher suites for TLS1.3. + """ + Accepted cipher suites for TLS1.3. + """ tls1_3CipherSuites: [CipherSuite] } - # Cipher suite information. + """ + Cipher suite information. + """ type CipherSuite { - # The name of the cipher suite + """ + The name of the cipher suite + """ name: String - # The strength of the cipher suite. + """ + The strength of the cipher suite. + """ strength: String } - # Elliptic curve information. + """ + Elliptic curve information. + """ type EllipticCurve { - # The name of the elliptic curve. + """ + The name of the elliptic curve. + """ name: String - # The strength of the elliptic curve. + """ + The strength of the elliptic curve. + """ strength: String } - # Results of HTTP connection scan on the given domain. + """ + Results of HTTP connection scan on the given domain. + """ type WebConnectionResult { - # The compliance status for HSTS for the scanned server from this scan. + """ + The compliance status for HSTS for the scanned server from this scan. + """ hstsStatus: String - # The compliance status for HTTPS for the scanned server from this scan. + """ + The compliance status for HTTPS for the scanned server from this scan. + """ httpsStatus: String - # Whether or not the server is serving data over HTTP. + """ + Whether or not the server is serving data over HTTP. + """ httpLive: Boolean - # Whether or not the server is serving data over HTTPS + """ + Whether or not the server is serving data over HTTPS + """ httpsLive: Boolean - # Whether or not HTTP connection was immediately upgraded (redirected) to HTTPS. + """ + Whether or not HTTP connection was immediately upgraded (redirected) to HTTPS. + """ httpImmediatelyUpgrades: Boolean - # Whether or not HTTP connection was eventually upgraded to HTTPS. + """ + Whether or not HTTP connection was eventually upgraded to HTTPS. + """ httpEventuallyUpgrades: Boolean - # Whether or not HTTPS connection is immediately downgraded to HTTP. + """ + Whether or not HTTPS connection is immediately downgraded to HTTP. + """ httpsImmediatelyDowngrades: Boolean - # Whether or not HTTPS connection is eventually downgraded to HTTP. + """ + Whether or not HTTPS connection is eventually downgraded to HTTP. + """ httpsEventuallyDowngrades: Boolean - # The parsed values for the HSTS header. + """ + The parsed values for the HSTS header. + """ hstsParsed: HSTSParsed - # The IP address for the scanned server. + """ + The IP address for the scanned server. + """ ipAddress: String - # The chain of connections created when visiting the domain using HTTP. + """ + The chain of connections created when visiting the domain using HTTP. + """ httpChainResult: ConnectionChainResult - # The chain of connections created when visiting the domain using HTTPS. + """ + The chain of connections created when visiting the domain using HTTPS. + """ httpsChainResult: ConnectionChainResult - # List of positive tags for the scanned server from this scan. + """ + List of positive tags for the scanned server from this scan. + """ positiveTags: [GuidanceTag] - # List of neutral tags for the scanned server from this scan. + """ + List of neutral tags for the scanned server from this scan. + """ neutralTags: [GuidanceTag] - # List of negative tags for the scanned server from this scan. + """ + List of negative tags for the scanned server from this scan. + """ negativeTags: [GuidanceTag] } - # The parsed values of the HSTS header. + """ + The parsed values of the HSTS header. + """ type HSTSParsed { - # How long to trust the HSTS header. + """ + How long to trust the HSTS header. + """ maxAge: Int - # Whether or not this HSTS policy should apply to subdomains. + """ + Whether or not this HSTS policy should apply to subdomains. + """ includeSubdomains: Boolean - # Whether or not the HSTS header includes the 'preload' option. + """ + Whether or not the HSTS header includes the 'preload' option. + """ preload: Boolean } - # Information collected while checking HTTP connections while following redirects. + """ + Information collected while checking HTTP connections while following redirects. + """ type ConnectionChainResult { - # The connection protocol used for the initial connection to the server (HTTP or HTTPS). + """ + The connection protocol used for the initial connection to the server (HTTP or HTTPS). + """ scheme: String - # The domain the scan was run on. + """ + The domain the scan was run on. + """ domain: String - # The initial full connection URI. + """ + The initial full connection URI. + """ uri: String - # Whether or not a redirection loop is created (causing endless redirects). + """ + Whether or not a redirection loop is created (causing endless redirects). + """ hasRedirectLoop: Boolean - # The connection chain created when following redirects. + """ + The connection chain created when following redirects. + """ connections: [Connection] } - # An HTTP (or HTTPS) connection. + """ + An HTTP (or HTTPS) connection. + """ type Connection { - # The URI for the given connection. + """ + The URI for the given connection. + """ uri: String - # Detailed information for a given connection. + """ + Detailed information for a given connection. + """ connection: ConnectionInfo - # Any errors which occurred when attempting to create this connection. + """ + Any errors which occurred when attempting to create this connection. + """ error: String - # The connection protocol used for this connection (HTTP or HTTPS). + """ + The connection protocol used for this connection (HTTP or HTTPS). + """ scheme: String } - # Detailed info for a given connection. + """ + Detailed info for a given connection. + """ type ConnectionInfo { - # The HTTP response status code. + """ + The HTTP response status code. + """ statusCode: Int - # The redirect location from the HTTP response. + """ + The redirect location from the HTTP response. + """ redirectTo: String - # The response headers from the HTTP response. The keys of the response are the header keys. + """ + The response headers from the HTTP response. The keys of the response are the header keys. + """ headers: JSONObject - # The detected category for the domain if blocked by firewall. + """ + The detected category for the domain if blocked by firewall. + """ blockedCategory: String - # Whether or not the response included an HSTS header. + """ + Whether or not the response included an HSTS header. + """ HSTS: Boolean } - # The 'JSONObject' scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). + """ + The 'JSONObject' scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). + """ scalar JSONObject - # Ordering options for web connections. + """ + Ordering options for web connections. + """ input WebOrder { - # The field to order web scans by. + """ + The field to order web scans by. + """ field: WebOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which web connections can be ordered. + """ + Properties by which web connections can be ordered. + """ enum WebOrderField { - # Order web edges by timestamp. + """ + Order web edges by timestamp. + """ TIMESTAMP } - # An enum used to select information from the dmarc-report-api. - enum PeriodEnums { - # The month of January. - JANUARY - - # The month of February. - FEBRUARY - - # The month of March. - MARCH - - # The month of April. - APRIL - - # The month of May. - MAY - - # The month of June. - JUNE - - # The month of July. - JULY - - # The month of August. - AUGUST - - # The month of September. - SEPTEMBER - - # The month of October. - OCTOBER - - # The month of November. - NOVEMBER - - # The month of December. - DECEMBER - - # The last 30 days. - LAST30DAYS - } - - # A field that conforms to a 4 digit integer. - scalar Year - - # This object displays the percentages of the category totals. + """ + This object displays the percentages of the category totals. + """ type CategoryPercentages { - # Percentage of messages that are failing all checks. + """ + Percentage of messages that are failing all checks. + """ failPercentage: Float - # Percentage of messages that are passing all checks. + """ + Percentage of messages that are passing all checks. + """ fullPassPercentage: Float - # Percentage of messages that are passing only dkim. + """ + Percentage of messages that are passing only dkim. + """ passDkimOnlyPercentage: Float - # Percentage of messages that are passing only spf. + """ + Percentage of messages that are passing only spf. + """ passSpfOnlyPercentage: Float - # The total amount of messages sent by this domain. + """ + The total amount of messages sent by this domain. + """ totalMessages: Int } - # This object displays the total amount of messages that fit into each category. + """ + This object displays the total amount of messages that fit into each category. + """ type CategoryTotals { - # Amount of messages that are passing SPF, but failing DKIM. + """ + Amount of messages that are passing SPF, but failing DKIM. + """ passSpfOnly: Int - # Amount of messages that are passing DKIM, but failing SPF. + """ + Amount of messages that are passing DKIM, but failing SPF. + """ passDkimOnly: Int - # Amount of messages that are passing SPF and DKIM. + """ + Amount of messages that are passing SPF and DKIM. + """ fullPass: Int - # Amount of messages that fail both SPF and DKIM. + """ + Amount of messages that fail both SPF and DKIM. + """ fail: Int } - # Object that contains the various senders and details for each category. + """ + Object that contains the various senders and details for each category. + """ type DetailTables { - # List of senders that are failing DKIM checks. + """ + List of senders that are failing DKIM checks. + """ dkimFailure( - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): DkimFailureTableConnection - # List of senders that are failing DMARC checks. + """ + List of senders that are failing DMARC checks. + """ dmarcFailure( - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): DmarcFailureTableConnection - # List of senders that are passing all checks. + """ + List of senders that are passing all checks. + """ fullPass( - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): FullPassTableConnection - # List of senders that are failing SPF checks. + """ + List of senders that are failing SPF checks. + """ spfFailure( - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): SpfFailureTableConnection } - # A connection to a list of items. + """ + A connection to a list of items. + """ type DkimFailureTableConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [DkimFailureTableEdge] - # The total amount of dkim failure the user has access to. + """ + The total amount of dkim failure the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type DkimFailureTableEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: DkimFailureTable - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # This table contains the data fields for senders who are in the DKIM fail category. + """ + This table contains the data fields for senders who are in the DKIM fail category. + """ type DkimFailureTable { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Is DKIM aligned. + """ + Is DKIM aligned. + """ dkimAligned: Boolean - # Domains used for DKIM validation + """ + Domains used for DKIM validation + """ dkimDomains: String - # The results of DKIM verification of the message. Can be pass, fail, neutral, temp-error, or perm-error. + """ + The results of DKIM verification of the message. Can be pass, fail, neutral, temp-error, or perm-error. + """ dkimResults: String - # Pointer to a DKIM public key record in DNS. + """ + Pointer to a DKIM public key record in DNS. + """ dkimSelectors: String - # Host from reverse DNS of source IP address. + """ + Host from reverse DNS of source IP address. + """ dnsHost: String - # Domain from SMTP banner message. + """ + Domain from SMTP banner message. + """ envelopeFrom: String - # Guidance for any issues that were found from the report. + """ + Guidance for any issues that were found from the report. + """ guidance: String @deprecated( reason: "This has been turned into the 'guidanceTag' field providing detailed information to act upon if a given tag is present." ) - # Guidance for any issues that were found from the report. + """ + Guidance for any issues that were found from the report. + """ guidanceTag: GuidanceTag - # The address/domain used in the "From" field. + """ + The address/domain used in the "From" field. + """ headerFrom: String - # IP address of sending server. + """ + IP address of sending server. + """ sourceIpAddress: String - # Total messages from this sender. + """ + Total messages from this sender. + """ totalMessages: Int } - # A connection to a list of items. + """ + A connection to a list of items. + """ type DmarcFailureTableConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [DmarcFailureTableEdge] - # The total amount of dmarc failures the user has access to. + """ + The total amount of dmarc failures the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type DmarcFailureTableEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: DmarcFailureTable - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # This table contains the data fields for senders who are in the DMARC failure category. + """ + This table contains the data fields for senders who are in the DMARC failure category. + """ type DmarcFailureTable { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Domains used for DKIM validation + """ + Domains used for DKIM validation + """ dkimDomains: String - # Pointer to a DKIM public key record in DNS. + """ + Pointer to a DKIM public key record in DNS. + """ dkimSelectors: String - # The DMARC enforcement action that the receiver took, either none, quarantine, or reject. + """ + The DMARC enforcement action that the receiver took, either none, quarantine, or reject. + """ disposition: String - # Host from reverse DNS of source IP address. + """ + Host from reverse DNS of source IP address. + """ dnsHost: String - # Domain from SMTP banner message. + """ + Domain from SMTP banner message. + """ envelopeFrom: String - # The address/domain used in the "From" field. + """ + The address/domain used in the "From" field. + """ headerFrom: String - # IP address of sending server. + """ + IP address of sending server. + """ sourceIpAddress: String - # Domains used for SPF validation. + """ + Domains used for SPF validation. + """ spfDomains: String - # Total messages from this sender. + """ + Total messages from this sender. + """ totalMessages: Int } - # A connection to a list of items. + """ + A connection to a list of items. + """ type FullPassTableConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [FullPassTableEdge] - # The total amount of full passes the user has access to. + """ + The total amount of full passes the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type FullPassTableEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: FullPassTable - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # This table contains the data fields for senders who are in the Full Pass category. + """ + This table contains the data fields for senders who are in the Full Pass category. + """ type FullPassTable { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Domains used for DKIM validation + """ + Domains used for DKIM validation + """ dkimDomains: String - # Pointer to a DKIM public key record in DNS. + """ + Pointer to a DKIM public key record in DNS. + """ dkimSelectors: String - # Host from reverse DNS of source IP address. + """ + Host from reverse DNS of source IP address. + """ dnsHost: String - # Domain from SMTP banner message. + """ + Domain from SMTP banner message. + """ envelopeFrom: String - # The address/domain used in the "From" field. + """ + The address/domain used in the "From" field. + """ headerFrom: String - # IP address of sending server. + """ + IP address of sending server. + """ sourceIpAddress: String - # Domains used for SPF validation. + """ + Domains used for SPF validation. + """ spfDomains: String - # Total messages from this sender. + """ + Total messages from this sender. + """ totalMessages: Int } - # A connection to a list of items. + """ + A connection to a list of items. + """ type SpfFailureTableConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [SpfFailureTableEdge] - # The total amount of spf failures the user has access to. + """ + The total amount of spf failures the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type SpfFailureTableEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: SpfFailureTable - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # This table contains the data fields for senders who are in the SPF fail category. + """ + This table contains the data fields for senders who are in the SPF fail category. + """ type SpfFailureTable { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Host from reverse DNS of source IP address. + """ + Host from reverse DNS of source IP address. + """ dnsHost: String - # Domain from SMTP banner message. + """ + Domain from SMTP banner message. + """ envelopeFrom: String - # Guidance for any issues that were found from the report. + """ + Guidance for any issues that were found from the report. + """ guidance: String @deprecated( reason: "This has been turned into the 'guidanceTag' field providing detailed information to act upon if a given tag is present." ) - # Guidance for any issues that were found from the report. + """ + Guidance for any issues that were found from the report. + """ guidanceTag: GuidanceTag - # The address/domain used in the "From" field. + """ + The address/domain used in the "From" field. + """ headerFrom: String - # IP address of sending server. + """ + IP address of sending server. + """ sourceIpAddress: String - # Is SPF aligned. + """ + Is SPF aligned. + """ spfAligned: Boolean - # Domains used for SPF validation. + """ + Domains used for SPF validation. + """ spfDomains: String - # The results of DKIM verification of the message. Can be pass, fail, neutral, soft-fail, temp-error, or perm-error. + """ + The results of DKIM verification of the message. Can be pass, fail, neutral, soft-fail, temp-error, or perm-error. + """ spfResults: String - # Total messages from this sender. + """ + Total messages from this sender. + """ totalMessages: Int } - # Ordering options for dmarc summary connections. + """ + Ordering options for dmarc summary connections. + """ input DmarcSummaryOrder { - # The field to order dmarc summaries by. + """ + The field to order dmarc summaries by. + """ field: DmarcSummaryOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which dmarc summary connections can be ordered. + """ + Properties by which dmarc summary connections can be ordered. + """ enum DmarcSummaryOrderField { - # Order dmarc summaries by fail count. + """ + Order dmarc summaries by fail count. + """ FAIL - # Order dmarc summaries by pass count. + """ + Order dmarc summaries by pass count. + """ FULL_PASS - # Order dmarc summaries by pass dkim only count. + """ + Order dmarc summaries by pass dkim only count. + """ PASS_DKIM_ONLY - # Order dmarc summaries by pass spf only count. + """ + Order dmarc summaries by pass spf only count. + """ PASS_SPF_ONLY - # Order dmarc summaries by fail percentage. + """ + Order dmarc summaries by fail percentage. + """ FAIL_PERCENTAGE - # Order dmarc summaries by pass percentage. + """ + Order dmarc summaries by pass percentage. + """ FULL_PASS_PERCENTAGE - # Order dmarc summaries by pass dkim only percentage. + """ + Order dmarc summaries by pass dkim only percentage. + """ PASS_DKIM_ONLY_PERCENTAGE - # Order dmarc summaries by spf only percentage. + """ + Order dmarc summaries by spf only percentage. + """ PASS_SPF_ONLY_PERCENTAGE - # Order dmarc summaries by total messages + """ + Order dmarc summaries by total messages + """ TOTAL_MESSAGES - # Order dmarc summaries by their respective domains. + """ + Order dmarc summaries by their respective domains. + """ DOMAIN } - # This object is used for showing personal user details, - # and is used for only showing the details of the querying user. + """ + A connection to a list of items. + """ + type ChartSummaryConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + + """ + A list of edges. + """ + edges: [ChartSummaryEdge] + + """ + The total amount of dmarc summaries the user has access to. + """ + totalCount: Int + } + + """ + An edge in a connection. + """ + type ChartSummaryEdge { + """ + The item at the end of the edge + """ + node: ChartSummary + + """ + A cursor for use in pagination + """ + cursor: String! + } + + """ + This object contains the information for each type of summary that has been pre-computed + """ + type ChartSummary { + """ + The ID of an object + """ + id: ID! + + """ + Date that the summary was computed. + """ + date: Date + + """ + https summary data + """ + https: CategorizedSummary + + """ + dmarc summary data + """ + dmarc: CategorizedSummary + + """ + Summary based on mail scan results for all domains. + """ + mail: CategorizedSummary + + """ + Summary based on web scan results for all domains. + """ + web: CategorizedSummary + + """ + Summary based on DMARC phases for all domains. + """ + dmarcPhase: CategorizedSummary + + """ + Summary based on SSL scan results for all domains. + """ + ssl: CategorizedSummary + + """ + Summary based on HTTPS and HSTS scan results for all domains. + """ + webConnections: CategorizedSummary + + """ + Summary based on SPF scan results for all domains. + """ + spf: CategorizedSummary + + """ + Summary based on DKIM scan results for all domains. + """ + dkim: CategorizedSummary + } + + """ + This object is used for showing personal user details, + and is used for only showing the details of the querying user. + """ type PersonalUser implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Users email address. + """ + Users email address. + """ userName: EmailAddress - # Name displayed to other users. + """ + Name displayed to other users. + """ displayName: String - # The phone number the user has setup with tfa. + """ + The phone number the user has setup with tfa. + """ phoneNumber: PhoneNumber - # Users preferred language. + """ + Users preferred language. + """ preferredLang: LanguageEnums - # Has the user completed phone validation. + """ + Has the user completed phone validation. + """ phoneValidated: Boolean - # Has the user email verified their account. + """ + Has the user email verified their account. + """ emailValidated: Boolean - # The method in which TFA codes are sent. + """ + The method in which TFA codes are sent. + """ tfaSendMethod: TFASendMethodEnum - # Does the user want to see new features in progress. + """ + Does the user want to see new features in progress. + """ insideUser: Boolean - # Does the user want to receive update emails. + """ + Does the user want to receive update emails. + """ receiveUpdateEmails: Boolean - # Users affiliations to various organizations. + """ + Users affiliations to various organizations. + """ affiliations( - # Ordering options for affiliation connections. + """ + Ordering options for affiliation connections. + """ orderBy: AffiliationOrgOrder - # String used to search for affiliated organizations. + """ + String used to search for affiliated organizations. + """ search: String - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): AffiliationConnection } - # A field whose value conforms to the standard E.164 format as specified in: https://en.wikipedia.org/wiki/E.164. Basically this is +17895551234. + """ + A field whose value conforms to the standard E.164 format as specified in: https://en.wikipedia.org/wiki/E.164. Basically this is +17895551234. + """ scalar PhoneNumber - # An enum used to define user's language. + """ + An enum used to define user's language. + """ enum LanguageEnums { - # Used for defining if English is the preferred language. + """ + Used for defining if English is the preferred language. + """ ENGLISH - # Used for defining if French is the preferred language. + """ + Used for defining if French is the preferred language. + """ FRENCH } enum TFASendMethodEnum { - # Used for defining that the TFA code will be sent via email. + """ + Used for defining that the TFA code will be sent via email. + """ EMAIL - # Used for defining that the TFA code will be sent via text. + """ + Used for defining that the TFA code will be sent via text. + """ PHONE - # User has not setup any TFA methods. + """ + User has not setup any TFA methods. + """ NONE } - # Organization object containing information for a given Organization. + """ + Organization object containing information for a given Organization. + """ type MyTrackerResult { - # Summaries based on scan types that are preformed on the given organizations domains. + """ + Summaries based on scan types that are preformed on the given organizations domains. + """ summaries: OrganizationSummary - # The number of domains associated with this organization. + """ + The number of domains associated with this organization. + """ domainCount: Int - # The domains which are associated with this organization. + """ + The domains which are associated with this organization. + """ domains( - # Ordering options for domain connections. + """ + Ordering options for domain connections. + """ orderBy: DomainOrder - # Limit domains to those that belong to an organization that has ownership. + """ + Limit domains to those that belong to an organization that has ownership. + """ ownership: Boolean - # Limits domains to those that user has added to their personal myTracker view. - myTracker: Boolean - - # String used to search for domains. + """ + String used to search for domains. + """ search: String - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): DomainConnection } - # A connection to a list of items. + """ + A connection to a list of items. + """ type UserConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [UserEdge] - # The total amount of users the user has access to. + """ + The total amount of users the user has access to. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type UserEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: SharedUser - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Domain object containing information for a given domain. + """ + Ordering options for affiliation connections. + """ + input UserOrder { + """ + The field to order affiliations by. + """ + field: UserOrderField! + + """ + The ordering direction. + """ + direction: OrderDirection! + } + + """ + Properties by which affiliation connections can be ordered. + """ + enum UserOrderField { + """ + Order affiliation edges by username. + """ + USER_USERNAME + + """ + Order affiliation edges by displayName. + """ + USER_DISPLAYNAME + + """ + Order affiliation edges by user verification status. + """ + USER_EMAIL_VALIDATED + + """ + Order affiliation edges by user insider status. + """ + USER_INSIDER + + """ + Order affiliation edges by amount of total affiliations. + """ + USER_AFFILIATIONS_COUNT + } + + """ + Domain object containing information for a given domain. + """ type VerifiedDomain implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # Domain that scans will be ran on. + """ + Domain that scans will be ran on. + """ domain: DomainScalar - # The last time that a scan was ran on this domain. + """ + The last time that a scan was ran on this domain. + """ lastRan: DateTime - # The domains scan status, based on the latest scan data. + """ + The domains scan status, based on the latest scan data. + """ status: DomainStatus - # The organization that this domain belongs to. + """ + The organization that this domain belongs to. + """ organizations( - # Ordering options for verified organization connections. + """ + Ordering options for verified organization connections. + """ orderBy: VerifiedOrganizationOrder - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): VerifiedOrganizationConnection } - # A connection to a list of items. + """ + A connection to a list of items. + """ type VerifiedOrganizationConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [VerifiedOrganizationEdge] - # The total amount of verified organizations. + """ + The total amount of verified organizations. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type VerifiedOrganizationEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: VerifiedOrganization - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Verified Organization object containing information for a given Organization. + """ + Verified Organization object containing information for a given Organization. + """ type VerifiedOrganization implements Node { - # The ID of an object + """ + The ID of an object + """ id: ID! - # The organizations acronym. + """ + The organizations acronym. + """ acronym: Acronym - # The full name of the organization. + """ + The full name of the organization. + """ name: String - # Slugified name of the organization. + """ + Slugified name of the organization. + """ slug: Slug - # The zone which the organization belongs to. + """ + The zone which the organization belongs to. + """ zone: String - # The sector which the organization belongs to. + """ + The sector which the organization belongs to. + """ sector: String - # The country in which the organization resides. + """ + The country in which the organization resides. + """ country: String - # The province in which the organization resides. + """ + The province in which the organization resides. + """ province: String - # The city in which the organization resides. + """ + The city in which the organization resides. + """ city: String - # Whether the organization is a verified organization. + """ + Whether the organization is a verified organization. + """ verified: Boolean - # Summaries based on scan types that are preformed on the given organizations domains. + """ + Summaries based on scan types that are preformed on the given organizations domains. + """ summaries: OrganizationSummary - # The number of domains associated with this organization. + """ + The number of domains associated with this organization. + """ domainCount: Int - # The domains which are associated with this organization. + """ + The domains which are associated with this organization. + """ domains( - # Ordering options for verified domain connections. + """ + Ordering options for verified domain connections. + """ orderBy: VerifiedDomainOrder - # Returns the items in the list that come after the specified cursor. + """ + Returns the items in the list that come after the specified cursor. + """ after: String - # Returns the first n items from the list. + """ + Returns the first n items from the list. + """ first: Int - # Returns the items in the list that come before the specified cursor. + """ + Returns the items in the list that come before the specified cursor. + """ before: String - # Returns the last n items from the list. + """ + Returns the last n items from the list. + """ last: Int ): VerifiedDomainConnection } - # A connection to a list of items. + """ + A connection to a list of items. + """ type VerifiedDomainConnection { - # Information to aid in pagination. + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - # A list of edges. + """ + A list of edges. + """ edges: [VerifiedDomainEdge] - # The total amount of verified domains. + """ + The total amount of verified domains. + """ totalCount: Int } - # An edge in a connection. + """ + An edge in a connection. + """ type VerifiedDomainEdge { - # The item at the end of the edge + """ + The item at the end of the edge + """ node: VerifiedDomain - # A cursor for use in pagination + """ + A cursor for use in pagination + """ cursor: String! } - # Ordering options for verified domain connections. + """ + Ordering options for verified domain connections. + """ input VerifiedDomainOrder { - # The field to order verified domains by. + """ + The field to order verified domains by. + """ field: VerifiedDomainOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which verified domain connections can be ordered. + """ + Properties by which verified domain connections can be ordered. + """ enum VerifiedDomainOrderField { - # Order verified domain edges by domain. + """ + Order verified domain edges by domain. + """ DOMAIN - # Order verified domain edges by last ran. + """ + Order verified domain edges by last ran. + """ LAST_RAN - # Order verified domain edges by dkim status. + """ + Order verified domain edges by dkim status. + """ DKIM_STATUS - # Order verified domain edges by dmarc status. + """ + Order verified domain edges by dmarc status. + """ DMARC_STATUS - # Order verified domain edges by https status. + """ + Order verified domain edges by https status. + """ HTTPS_STATUS - # Order verified domain edges by spf status. + """ + Order verified domain edges by spf status. + """ SPF_STATUS - # Order verified domain edges by ssl status. + """ + Order verified domain edges by ssl status. + """ SSL_STATUS } - # Ordering options for verified organization connections. + """ + Ordering options for verified organization connections. + """ input VerifiedOrganizationOrder { - # The field to order verified organizations by. + """ + The field to order verified organizations by. + """ field: VerifiedOrganizationOrderField! - # The ordering direction. + """ + The ordering direction. + """ direction: OrderDirection! } - # Properties by which verified organization connections can be ordered. + """ + Properties by which verified organization connections can be ordered. + """ enum VerifiedOrganizationOrderField { - # Order verified organization edges by acronym. + """ + Order verified organization edges by acronym. + """ ACRONYM - # Order verified organization edges by name. + """ + Order verified organization edges by name. + """ NAME - # Order verified organization edges by zone. + """ + Order verified organization edges by zone. + """ ZONE - # Order verified organization edges by sector. + """ + Order verified organization edges by sector. + """ SECTOR - # Order verified organization edges by country. + """ + Order verified organization edges by country. + """ COUNTRY - # Order verified organizations by summary mail pass count. + """ + Order verified organizations by summary mail pass count. + """ SUMMARY_MAIL_PASS - # Order verified organizations by summary mail fail count. + """ + Order verified organizations by summary mail fail count. + """ SUMMARY_MAIL_FAIL - # Order verified organizations by summary mail total count. + """ + Order verified organizations by summary mail total count. + """ SUMMARY_MAIL_TOTAL - # Order verified organizations by summary web pass count. + """ + Order verified organizations by summary web pass count. + """ SUMMARY_WEB_PASS - # Order verified organizations by summary web fail count. + """ + Order verified organizations by summary web fail count. + """ SUMMARY_WEB_FAIL - # Order verified organizations by summary web total count. + """ + Order verified organizations by summary web total count. + """ SUMMARY_WEB_TOTAL - # Order verified organizations by domain count. + """ + Order verified organizations by domain count. + """ DOMAIN_COUNT } type Mutation { - # This mutation allows admins and higher to invite users to any of their - # organizations, if the invited user does not have an account, they will be - # able to sign-up and be assigned to that organization in one mutation. + """ + This mutation allows admins and higher to invite users to any of their + organizations, if the invited user does not have an account, they will be + able to sign-up and be assigned to that organization in one mutation. + """ inviteUserToOrg(input: InviteUserToOrgInput!): InviteUserToOrgPayload - # This mutation allows users to leave a given organization. + """ + This mutation allows users to leave a given organization. + """ leaveOrganization(input: LeaveOrganizationInput!): LeaveOrganizationPayload - # This mutation allows admins or higher to remove users from any organizations they belong to. + """ + This mutation allows admins or higher to remove users from any organizations they belong to. + """ removeUserFromOrg(input: RemoveUserFromOrgInput!): RemoveUserFromOrgPayload - # This mutation allows users to request to join an organization. + """ + This mutation allows users to request to join an organization. + """ requestOrgAffiliation(input: RequestOrgAffiliationInput!): RequestOrgAffiliationPayload - # This mutation allows a user to transfer org ownership to another user in the given org. + """ + This mutation allows a user to transfer org ownership to another user in the given org. + """ transferOrgOwnership(input: TransferOrgOwnershipInput!): TransferOrgOwnershipPayload - # This mutation allows super admins, and admins of the given organization to - # update the permission level of a given user that already belongs to the - # given organization. + """ + This mutation allows super admins, and admins of the given organization to + update the permission level of a given user that already belongs to the + given organization. + """ updateUserRole(input: UpdateUserRoleInput!): UpdateUserRolePayload - # Mutation used to create multiple new domains for an organization. + """ + Mutation used to create multiple new domains for an organization. + """ addOrganizationsDomains(input: AddOrganizationsDomainsInput!): AddOrganizationsDomainsPayload - # Mutation used to create a new domain for an organization. + """ + Mutation used to create a new domain for an organization. + """ createDomain(input: CreateDomainInput!): CreateDomainPayload - # Mutation to add domain to user's personal myTracker view. + """ + Mutation to add domain to user's personal myTracker view. + """ favouriteDomain(input: FavouriteDomainInput!): FavouriteDomainPayload - # This mutation allows the removal of unused domains. + """ + This mutation allows the removal of unused domains. + """ removeDomain(input: RemoveDomainInput!): RemoveDomainPayload - # This mutation allows the removal of unused domains. + """ + This mutation allows the removal of unused domains. + """ removeOrganizationsDomains(input: RemoveOrganizationsDomainsInput!): RemoveOrganizationsDomainsPayload - # This mutation is used to start a subdomain discovery scan on a requested domain. + """ + This mutation is used to start a subdomain discovery scan on a requested domain. + """ requestDiscovery(input: RequestDiscoveryInput!): RequestDiscoveryPayload - # This mutation is used to start a manual scan on a requested domain. + """ + This mutation is used to start a manual scan on a requested domain. + """ requestScan(input: RequestScanInput!): RequestScanPayload - # Mutation to remove domain from user's personal myTracker view. + """ + Mutation to remove domain from user's personal myTracker view. + """ unfavouriteDomain(input: UnfavouriteDomainInput!): UnfavouriteDomainPayload - # Mutation allows the modification of domains if domain is updated through out its life-cycle + """ + Mutation allows the modification of domains if domain is updated through out its life-cycle + """ updateDomain(input: UpdateDomainInput!): UpdateDomainPayload - # This mutation allows the archival of unused organizations. + """ + This mutation allows the archival of unused organizations. + """ archiveOrganization(input: ArchiveOrganizationInput!): ArchiveOrganizationPayload - # This mutation allows the creation of an organization inside the database. + """ + This mutation allows the creation of an organization inside the database. + """ createOrganization(input: CreateOrganizationInput!): CreateOrganizationPayload - # This mutation allows the removal of unused organizations. + """ + This mutation allows the removal of unused organizations. + """ removeOrganization(input: RemoveOrganizationInput!): RemoveOrganizationPayload - # Mutation allows the modification of organizations if any changes to the organization may occur. + """ + Mutation allows the modification of organizations if any changes to the organization may occur. + """ updateOrganization(input: UpdateOrganizationInput!): UpdateOrganizationPayload - # Mutation allows the verification of an organization. + """ + Mutation allows the verification of an organization. + """ verifyOrganization(input: VerifyOrganizationInput!): VerifyOrganizationPayload - # This mutation allows users to give their credentials and retrieve a token that gives them access to restricted content. + """ + This mutation allows users to give their credentials and retrieve a token that gives them access to restricted content. + """ authenticate(input: AuthenticateInput!): AuthenticatePayload - # This mutation allows a super admin to close another user's account. + """ + This mutation allows a super admin to close another user's account. + """ closeAccountOther(input: CloseAccountOtherInput!): CloseAccountOtherPayload - # This mutation allows a user to close their account. + """ + This mutation allows a user to close their account. + """ closeAccountSelf(input: CloseAccountSelfInput!): CloseAccountSelfPayload - # This mutation allows users to give their current auth token, and refresh token, and receive a freshly updated auth token. + """ + This mutation allows users to give their current auth token, and refresh token, and receive a freshly updated auth token. + """ refreshTokens(input: RefreshTokensInput!): RefreshTokensPayload - # This mutation allows for users to remove a phone number from their account. + """ + This mutation allows for users to remove a phone number from their account. + """ removePhoneNumber(input: RemovePhoneNumberInput!): RemovePhoneNumberPayload - # This mutation allows the user to take the token they received in their email to reset their password. + """ + This mutation allows the user to take the token they received in their email to reset their password. + """ resetPassword(input: ResetPasswordInput!): ResetPasswordPayload - # This mutation is used for re-sending a verification email if it failed during user creation. + """ + This mutation is used for re-sending a verification email if it failed during user creation. + """ sendEmailVerification(input: SendEmailVerificationInput!): SendEmailVerificationPayload - # This mutation allows a user to provide their username and request that a password reset email be sent to their account with a reset token in a url. + """ + This mutation allows a user to provide their username and request that a password reset email be sent to their account with a reset token in a url. + """ sendPasswordResetLink(input: SendPasswordResetLinkInput!): SendPasswordResetLinkPayload - # This mutation is used for setting a new phone number for a user, and sending a code for verifying the new number. + """ + This mutation is used for setting a new phone number for a user, and sending a code for verifying the new number. + """ setPhoneNumber(input: SetPhoneNumberInput!): SetPhoneNumberPayload - # This mutation allows users to give their credentials and either signed in, re-directed to the tfa auth page, or given an error. + """ + This mutation allows users to give their credentials and either signed in, re-directed to the tfa auth page, or given an error. + """ signIn(input: SignInInput!): SignInPayload - # This mutation allows a user to sign out, and clear their cookies. + """ + This mutation allows a user to sign out, and clear their cookies. + """ signOut(input: SignOutInput!): SignOutPayload - # This mutation allows for new users to sign up for our sites services. + """ + This mutation allows for new users to sign up for our sites services. + """ signUp(input: SignUpInput!): SignUpPayload - # This mutation allows the user to update their account password. + """ + This mutation allows the user to update their account password. + """ updateUserPassword(input: UpdateUserPasswordInput!): UpdateUserPasswordPayload - # This mutation allows the user to update their user profile to change various details of their current profile. + """ + This mutation allows the user to update their user profile to change various details of their current profile. + """ updateUserProfile(input: UpdateUserProfileInput!): UpdateUserProfilePayload - # This mutation allows the user to verify their account through a token sent in an email. + """ + This mutation allows the user to verify their account through a token sent in an email. + """ verifyAccount(input: VerifyAccountInput!): VerifyAccountPayload - # This mutation allows the user to two factor authenticate. + """ + This mutation allows the user to two factor authenticate. + """ verifyPhoneNumber(input: verifyPhoneNumberInput!): verifyPhoneNumberPayload } type InviteUserToOrgPayload { - # 'InviteUserToOrgUnion' returning either a 'InviteUserToOrgResult', or 'InviteUserToOrgError' object. + """ + 'InviteUserToOrgUnion' returning either a 'InviteUserToOrgResult', or 'InviteUserToOrgError' object. + """ result: InviteUserToOrgUnion clientMutationId: String } - # This union is used with the 'InviteUserToOrg' mutation, allowing for users to invite user to their org, and support any errors that may occur + """ + This union is used with the 'InviteUserToOrg' mutation, allowing for users to invite user to their org, and support any errors that may occur + """ union InviteUserToOrgUnion = AffiliationError | InviteUserToOrgResult - # This object is used to inform the user if any errors occurred while executing affiliation mutations. + """ + This object is used to inform the user if any errors occurred while executing affiliation mutations. + """ type AffiliationError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user of the invitation status. + """ + This object is used to inform the user of the invitation status. + """ type InviteUserToOrgResult { - # Informs the user if the invite or invite email was successfully sent. + """ + Informs the user if the invite or invite email was successfully sent. + """ status: String } input InviteUserToOrgInput { - # Users email that you would like to invite to your org. + """ + Users email that you would like to invite to your org. + """ userName: EmailAddress! - # The role which you would like this user to have. + """ + The role which you would like this user to have. + """ requestedRole: InvitationRoleEnums! - # The organization you wish to invite the user to. + """ + The organization you wish to invite the user to. + """ orgId: ID! clientMutationId: String } - # An enum used when inviting users to an organization to assign their role. + """ + An enum used when inviting users to an organization to assign their role. + """ enum InvitationRoleEnums { - # A user who has been given access to view an organization. + """ + A user who has been given access to view an organization. + """ USER - # A user who has the same access as a user write account, but can define new user read/write accounts. + """ + A user who has the same access as a user write account, but can define new user read/write accounts. + """ ADMIN - # A user who has the same access as an admin, but can define new admins. + """ + A user who has the same access as an admin, but can define new admins, and delete the organization. + """ + OWNER + + """ + A user who has the same access as an admin, but can define new admins. + """ SUPER_ADMIN } type LeaveOrganizationPayload { - # 'LeaveOrganizationUnion' resolving to either a 'LeaveOrganizationResult' or 'AffiliationError'. + """ + 'LeaveOrganizationUnion' resolving to either a 'LeaveOrganizationResult' or 'AffiliationError'. + """ result: LeaveOrganizationUnion clientMutationId: String } - # This union is used with the 'leaveOrganization' mutation, allowing for users to leave a given organization, and support any errors that may occur. + """ + This union is used with the 'leaveOrganization' mutation, allowing for users to leave a given organization, and support any errors that may occur. + """ union LeaveOrganizationUnion = AffiliationError | LeaveOrganizationResult - # This object is used to inform the user that they successful left a given organization. + """ + This object is used to inform the user that they successful left a given organization. + """ type LeaveOrganizationResult { - # Status message confirming the user left the org. + """ + Status message confirming the user left the org. + """ status: String } input LeaveOrganizationInput { - # Id of the organization the user is looking to leave. + """ + Id of the organization the user is looking to leave. + """ orgId: ID! clientMutationId: String } type RemoveUserFromOrgPayload { - # 'RemoveUserFromOrgUnion' returning either a 'RemoveUserFromOrgResult', or 'RemoveUserFromOrgError' object. + """ + 'RemoveUserFromOrgUnion' returning either a 'RemoveUserFromOrgResult', or 'RemoveUserFromOrgError' object. + """ result: RemoveUserFromOrgUnion clientMutationId: String } - # This union is used with the 'RemoveUserFromOrg' mutation, allowing for users to remove a user from their org, and support any errors that may occur + """ + This union is used with the 'RemoveUserFromOrg' mutation, allowing for users to remove a user from their org, and support any errors that may occur + """ union RemoveUserFromOrgUnion = AffiliationError | RemoveUserFromOrgResult - # This object is used to inform the user of the removal status. + """ + This object is used to inform the user of the removal status. + """ type RemoveUserFromOrgResult { - # Informs the user if the user was successfully removed. + """ + Informs the user if the user was successfully removed. + """ status: String - # The user that was just removed. + """ + The user that was just removed. + """ user: SharedUser } input RemoveUserFromOrgInput { - # The user id of the user to be removed. + """ + The user id of the user to be removed. + """ userId: ID! - # The organization that the user is to be removed from. + """ + The organization that the user is to be removed from. + """ orgId: ID! clientMutationId: String } type RequestOrgAffiliationPayload { - # 'InviteUserToOrgUnion' returning either a 'InviteUserToOrgResult', or 'InviteUserToOrgError' object. + """ + 'InviteUserToOrgUnion' returning either a 'InviteUserToOrgResult', or 'InviteUserToOrgError' object. + """ result: InviteUserToOrgUnion clientMutationId: String } input RequestOrgAffiliationInput { - # The organization you wish to invite the user to. + """ + The organization you wish to invite the user to. + """ orgId: ID! clientMutationId: String } type TransferOrgOwnershipPayload { - # 'TransferOrgOwnershipUnion' resolving to either a 'TransferOrgOwnershipResult' or 'AffiliationError'. + """ + 'TransferOrgOwnershipUnion' resolving to either a 'TransferOrgOwnershipResult' or 'AffiliationError'. + """ result: TransferOrgOwnershipUnion clientMutationId: String } - # This union is used with the 'transferOrgOwnership' mutation, allowing for - # users to transfer ownership of a given organization, and support any errors that may occur. + """ + This union is used with the 'transferOrgOwnership' mutation, allowing for + users to transfer ownership of a given organization, and support any errors that may occur. + """ union TransferOrgOwnershipUnion = AffiliationError | TransferOrgOwnershipResult - # This object is used to inform the user that they successful transferred ownership of a given organization. + """ + This object is used to inform the user that they successful transferred ownership of a given organization. + """ type TransferOrgOwnershipResult { - # Status message confirming the user transferred ownership of the org. + """ + Status message confirming the user transferred ownership of the org. + """ status: String } input TransferOrgOwnershipInput { - # Id of the organization the user is looking to transfer ownership of. + """ + Id of the organization the user is looking to transfer ownership of. + """ orgId: ID! - # Id of the user that the org ownership is being transferred to. + """ + Id of the user that the org ownership is being transferred to. + """ userId: ID! clientMutationId: String } type UpdateUserRolePayload { - # 'UpdateUserRoleUnion' returning either a 'UpdateUserRoleResult', or 'UpdateUserRoleError' object. + """ + 'UpdateUserRoleUnion' returning either a 'UpdateUserRoleResult', or 'UpdateUserRoleError' object. + """ result: UpdateUserRoleUnion clientMutationId: String } - # This union is used with the 'UpdateUserRole' mutation, allowing for users to update a users role in an org, and support any errors that may occur + """ + This union is used with the 'UpdateUserRole' mutation, allowing for users to update a users role in an org, and support any errors that may occur + """ union UpdateUserRoleUnion = AffiliationError | UpdateUserRoleResult - # This object is used to inform the user of the status of the role update. + """ + This object is used to inform the user of the status of the role update. + """ type UpdateUserRoleResult { - # Informs the user if the user who's role was successfully updated. + """ + Informs the user if the user who's role was successfully updated. + """ status: String - # The user who's role was successfully updated. + """ + The user who's role was successfully updated. + """ user: SharedUser } input UpdateUserRoleInput { - # The username of the user you wish to update their role to. + """ + The username of the user you wish to update their role to. + """ userName: EmailAddress! - # The organization that the admin, and the user both belong to. + """ + The organization that the admin, and the user both belong to. + """ orgId: ID! - # The role that the admin wants to give to the selected user. + """ + The role that the admin wants to give to the selected user. + """ role: RoleEnums! clientMutationId: String } type AddOrganizationsDomainsPayload { - # 'BulkModifyDomainsUnion' returning either a 'DomainBulkResult', or 'DomainErrorType' object. + """ + 'BulkModifyDomainsUnion' returning either a 'DomainBulkResult', or 'DomainErrorType' object. + """ result: BulkModifyDomainsUnion clientMutationId: String } - # This union is used with the 'AddOrganizationsDomains' and 'RemoveOrganizationsDomains' mutation, - # allowing for users to add/remove multiple domains belonging to their org, - # and support any errors that may occur + """ + This union is used with the 'AddOrganizationsDomains' and 'RemoveOrganizationsDomains' mutation, + allowing for users to add/remove multiple domains belonging to their org, + and support any errors that may occur + """ union BulkModifyDomainsUnion = DomainError | DomainBulkResult - # This object is used to inform the user if any errors occurred while using a domain mutation. + """ + This object is used to inform the user if any errors occurred while using a domain mutation. + """ type DomainError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while mutating a domain. + """ + This object is used to inform the user that no errors were encountered while mutating a domain. + """ type DomainBulkResult { - # Informs the user if the domain mutation was successful. + """ + Informs the user if the domain mutation was successful. + """ status: String } input AddOrganizationsDomainsInput { - # The global id of the organization you wish to assign this domain to. + """ + The global id of the organization you wish to assign this domain to. + """ orgId: ID! - # Url that you would like to be added to the database. + """ + Url that you would like to be added to the database. + """ domains: [DomainScalar]! - # New domains will be hidden. + """ + New domains will be hidden. + """ hideNewDomains: Boolean - # New domains will be tagged with NEW. + """ + New domains will be tagged with NEW. + """ tagNewDomains: Boolean - # New domains will be tagged with STAGING. + """ + New domains will be tagged with STAGING. + """ tagStagingDomains: Boolean - # Audit logs will be created. + """ + Audit logs will be created. + """ audit: Boolean clientMutationId: String } type CreateDomainPayload { - # 'CreateDomainUnion' returning either a 'Domain', or 'CreateDomainError' object. + """ + 'CreateDomainUnion' returning either a 'Domain', or 'CreateDomainError' object. + """ result: CreateDomainUnion clientMutationId: String } - # This union is used with the 'CreateDomain' mutation, - # allowing for users to create a domain and add it to their org, - # and support any errors that may occur + """ + This union is used with the 'CreateDomain' mutation, + allowing for users to create a domain and add it to their org, + and support any errors that may occur + """ union CreateDomainUnion = DomainError | Domain input CreateDomainInput { - # The global id of the organization you wish to assign this domain to. + """ + The global id of the organization you wish to assign this domain to. + """ orgId: ID! - # Url that you would like to be added to the database. + """ + Url that you would like to be added to the database. + """ domain: DomainScalar! - # DKIM selector strings corresponding to this domain. - selectors: [Selector] + """ + DKIM selector strings corresponding to this domain. + """ + selectors: [SelectorInput] - # List of labelled tags users have applied to the domain. + """ + List of labelled tags users have applied to the domain. + """ tags: [InputTag] - # Value that determines if the domain is excluded from an organization's score. + """ + Value that determines if the domain is excluded from an organization's score. + """ hidden: Boolean - # Value that determines if the domain is excluded from the scanning process. + """ + Value that determines if the domain is excluded from the scanning process. + """ archived: Boolean - # Comment describing reason for adding out-of-scope domain. + """ + Comment describing reason for adding out-of-scope domain. + """ outsideComment: OutsideDomainCommentEnum clientMutationId: String } - # User-generated tag assigned to domains for labeling and management. + """ + A field that conforms to a DKIM selector for input. Must be either a single asterisk or a string where only alphanumeric characters and periods are allowed, string must also start and end with alphanumeric characters + """ + scalar SelectorInput + + """ + User-generated tag assigned to domains for labeling and management. + """ input InputTag { - # The English translation of the label. + """ + The English translation of the label. + """ en: DomainTagLabel! - # The French translation of the label. + """ + The French translation of the label. + """ fr: DomainTagLabel! } - # An enum used to assign and test user-generated domain tags + """ + An enum used to assign and test user-generated domain tags + """ enum DomainTagLabel { - # English label for tagging domains as new to the system. + """ + English label for tagging domains as new to the system. + """ NEW - # French label for tagging domains as new to the system. + """ + French label for tagging domains as new to the system. + """ NOUVEAU - # Bilingual Label for tagging domains as a production environment. + """ + Bilingual Label for tagging domains as a production environment. + """ PROD - # English label for tagging domains as a staging environment. + """ + English label for tagging domains as a staging environment. + """ STAGING - # French label for tagging domains as a staging environment. + """ + French label for tagging domains as a staging environment. + """ DEV - # Bilingual label for tagging domains as a test environment. + """ + Bilingual label for tagging domains as a test environment. + """ TEST - # Bilingual label for tagging domains as web-hosting. + """ + Bilingual label for tagging domains as web-hosting. + """ WEB - # English label for tagging domains that are not active. + """ + English label for tagging domains that are not active. + """ INACTIVE - # French label for tagging domains that are not active. + """ + French label for tagging domains that are not active. + """ INACTIF - # English label for tagging domains that are hidden. + """ + English label for tagging domains that are hidden. + """ HIDDEN - # English label for tagging domains that are archived. + """ + English label for tagging domains that are archived. + """ ARCHIVED - # Label for tagging domains that have an rcode status of NXDOMAIN. + """ + Label for tagging domains that have an rcode status of NXDOMAIN. + """ NXDOMAIN - # Label for tagging domains that are possibly blocked by a firewall. + """ + Label for tagging domains that are possibly blocked by a firewall. + """ BLOCKED - # Label for tagging domains that have a wildcard sibling. + """ + Label for tagging domains that have a wildcard sibling. + """ WILDCARD_SIBLING - # Label for tagging domains that have a pending web scan. + """ + Label for tagging domains that have a pending web scan. + """ SCAN_PENDING - # English label for tagging domains that are outside the scope of the project. + """ + English label for tagging domains that are outside the scope of the project. + """ OUTSIDE - # French label for tagging domains that are outside the scope of the project. + """ + French label for tagging domains that are outside the scope of the project. + """ EXTERIEUR } - # Reason why an outside domain was added to the organization. + """ + Reason why an outside domain was added to the organization. + """ enum OutsideDomainCommentEnum { - # Organization is invested in the outside domain. + """ + Organization is invested in the outside domain. + """ INVESTMENT - # Organization owns this domain, but it is outside the allowed scope. + """ + Organization owns this domain, but it is outside the allowed scope. + """ OWNERSHIP - # Other reason. + """ + Other reason. + """ OTHER } type FavouriteDomainPayload { - # 'CreateDomainUnion' returning either a 'Domain', or 'CreateDomainError' object. + """ + 'CreateDomainUnion' returning either a 'Domain', or 'CreateDomainError' object. + """ result: CreateDomainUnion clientMutationId: String } input FavouriteDomainInput { - # The global id of the domain you wish to favourite. + """ + The global id of the domain you wish to favourite. + """ domainId: ID! clientMutationId: String } type RemoveDomainPayload { - # 'RemoveDomainUnion' returning either a 'DomainResultType', or 'DomainErrorType' object. + """ + 'RemoveDomainUnion' returning either a 'DomainResultType', or 'DomainErrorType' object. + """ result: RemoveDomainUnion! clientMutationId: String } - # This union is used with the 'RemoveDomain' mutation, - # allowing for users to remove a domain belonging to their org, - # and support any errors that may occur + """ + This union is used with the 'RemoveDomain' mutation, + allowing for users to remove a domain belonging to their org, + and support any errors that may occur + """ union RemoveDomainUnion = DomainError | DomainResult - # This object is used to inform the user that no errors were encountered while mutating a domain. + """ + This object is used to inform the user that no errors were encountered while mutating a domain. + """ type DomainResult { - # Informs the user if the domain mutation was successful. + """ + Informs the user if the domain mutation was successful. + """ status: String - # The domain that is being mutated. + """ + The domain that is being mutated. + """ domain: Domain } input RemoveDomainInput { - # The global id of the domain you wish to remove. + """ + The global id of the domain you wish to remove. + """ domainId: ID! - # The organization you wish to remove the domain from. + """ + The organization you wish to remove the domain from. + """ orgId: ID! - # The reason given for why this domain is being removed from the organization. + """ + The reason given for why this domain is being removed from the organization. + """ reason: DomainRemovalReasonEnum! clientMutationId: String } type RemoveOrganizationsDomainsPayload { - # 'BulkModifyDomainsUnion' returning either a 'DomainBulkResult', or 'DomainErrorType' object. + """ + 'BulkModifyDomainsUnion' returning either a 'DomainBulkResult', or 'DomainErrorType' object. + """ result: BulkModifyDomainsUnion! clientMutationId: String } input RemoveOrganizationsDomainsInput { - # Domains you wish to remove from the organization. + """ + Domains you wish to remove from the organization. + """ domains: [DomainScalar]! - # The organization you wish to remove the domain from. + """ + The organization you wish to remove the domain from. + """ orgId: ID! - # Domains will be archived. + """ + Domains will be archived. + """ archiveDomains: Boolean - # Audit logs will be created. + """ + Audit logs will be created. + """ audit: Boolean clientMutationId: String } type RequestDiscoveryPayload { - # Informs the user if the scan was dispatched successfully. + """ + Informs the user if the scan was dispatched successfully. + """ status: String clientMutationId: String } input RequestDiscoveryInput { - # The base domain that the subdomain scan will be ran on. + """ + The base domain that the subdomain scan will be ran on. + """ domain: DomainScalar - # The global id of the organization you wish to assign new found domains to. + """ + The global id of the organization you wish to assign new found domains to. + """ orgId: ID! clientMutationId: String } type RequestScanPayload { - # Informs the user if the scan was dispatched successfully. + """ + Informs the user if the scan was dispatched successfully. + """ status: String clientMutationId: String } input RequestScanInput { - # The domain that the scan will be ran on. + """ + The domain that the scan will be ran on. + """ domain: DomainScalar clientMutationId: String } type UnfavouriteDomainPayload { - # 'RemoveDomainUnion' returning either a 'DomainResultType', or 'DomainErrorType' object. + """ + 'RemoveDomainUnion' returning either a 'DomainResultType', or 'DomainErrorType' object. + """ result: RemoveDomainUnion! clientMutationId: String } input UnfavouriteDomainInput { - # The global id of the domain you wish to favourite. + """ + The global id of the domain you wish to favourite. + """ domainId: ID! clientMutationId: String } type UpdateDomainPayload { - # 'UpdateDomainUnion' returning either a 'Domain', or 'DomainError' object. + """ + 'UpdateDomainUnion' returning either a 'Domain', or 'DomainError' object. + """ result: UpdateDomainUnion clientMutationId: String } - # This union is used with the 'UpdateDomain' mutation, - # allowing for users to update a domain belonging to their org, - # and support any errors that may occur + """ + This union is used with the 'UpdateDomain' mutation, + allowing for users to update a domain belonging to their org, + and support any errors that may occur + """ union UpdateDomainUnion = DomainError | Domain input UpdateDomainInput { - # The global id of the domain that is being updated. + """ + The global id of the domain that is being updated. + """ domainId: ID! - # The global ID of the organization used for permission checks. + """ + The global ID of the organization used for permission checks. + """ orgId: ID! - # The new url of the of the old domain. + """ + The new url of the of the old domain. + """ domain: DomainScalar - # The updated DKIM selector strings corresponding to this domain. - selectors: [Selector] + """ + The updated DKIM selector strings corresponding to this domain. + """ + selectors: [SelectorInput] - # List of labelled tags users have applied to the domain. + """ + List of labelled tags users have applied to the domain. + """ tags: [InputTag] - # Value that determines if the domain is excluded from an organization's score. + """ + Value that determines if the domain is excluded from an organization's score. + """ hidden: Boolean - # Value that determines if the domain is excluded from the scanning process. + """ + Value that determines if the domain is excluded from the scanning process. + """ archived: Boolean - # Comment describing reason for adding out-of-scope domain. + """ + Comment describing reason for adding out-of-scope domain. + """ outsideComment: OutsideDomainCommentEnum clientMutationId: String } type ArchiveOrganizationPayload { - # 'RemoveOrganizationUnion' returning either an 'OrganizationResult', or 'OrganizationError' object. + """ + 'RemoveOrganizationUnion' returning either an 'OrganizationResult', or 'OrganizationError' object. + """ result: RemoveOrganizationUnion! clientMutationId: String } - # This union is used with the 'RemoveOrganization' mutation, - # allowing for users to remove an organization they belong to, - # and support any errors that may occur + """ + This union is used with the 'RemoveOrganization' mutation, + allowing for users to remove an organization they belong to, + and support any errors that may occur + """ union RemoveOrganizationUnion = OrganizationError | OrganizationResult - # This object is used to inform the user if any errors occurred while using an organization mutation. + """ + This object is used to inform the user if any errors occurred while using an organization mutation. + """ type OrganizationError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while running organization mutations. + """ + This object is used to inform the user that no errors were encountered while running organization mutations. + """ type OrganizationResult { - # Informs the user if the organization mutation was successful. + """ + Informs the user if the organization mutation was successful. + """ status: String - # The organization that was being affected by the mutation. + """ + The organization that was being affected by the mutation. + """ organization: Organization } input ArchiveOrganizationInput { - # The global id of the organization you wish you archive. + """ + The global id of the organization you wish you archive. + """ orgId: ID! clientMutationId: String } type CreateOrganizationPayload { - # 'CreateOrganizationUnion' returning either an 'Organization', or 'OrganizationError' object. + """ + 'CreateOrganizationUnion' returning either an 'Organization', or 'OrganizationError' object. + """ result: CreateOrganizationUnion clientMutationId: String } - # This union is used with the 'CreateOrganization' mutation, - # allowing for users to create an organization, and support any errors that may occur + """ + This union is used with the 'CreateOrganization' mutation, + allowing for users to create an organization, and support any errors that may occur + """ union CreateOrganizationUnion = OrganizationError | Organization input CreateOrganizationInput { - # The English acronym of the organization. + """ + The English acronym of the organization. + """ acronymEN: Acronym! - # The French acronym of the organization. + """ + The French acronym of the organization. + """ acronymFR: Acronym! - # The English name of the organization. + """ + The English name of the organization. + """ nameEN: String! - # The French name of the organization. + """ + The French name of the organization. + """ nameFR: String! - # The English translation of the zone the organization belongs to. + """ + The English translation of the zone the organization belongs to. + """ zoneEN: String! - # The English translation of the zone the organization belongs to. + """ + The English translation of the zone the organization belongs to. + """ zoneFR: String! - # The English translation of the sector the organization belongs to. + """ + The English translation of the sector the organization belongs to. + """ sectorEN: String! - # The French translation of the sector the organization belongs to. + """ + The French translation of the sector the organization belongs to. + """ sectorFR: String! - # The English translation of the country the organization resides in. + """ + The English translation of the country the organization resides in. + """ countryEN: String! - # The French translation of the country the organization resides in. + """ + The French translation of the country the organization resides in. + """ countryFR: String! - # The English translation of the province the organization resides in. + """ + The English translation of the province the organization resides in. + """ provinceEN: String! - # The French translation of the province the organization resides in. + """ + The French translation of the province the organization resides in. + """ provinceFR: String! - # The English translation of the city the organization resides in. + """ + The English translation of the city the organization resides in. + """ cityEN: String! - # The French translation of the city the organization resides in. + """ + The French translation of the city the organization resides in. + """ cityFR: String! clientMutationId: String } type RemoveOrganizationPayload { - # 'RemoveOrganizationUnion' returning either an 'OrganizationResult', or 'OrganizationError' object. + """ + 'RemoveOrganizationUnion' returning either an 'OrganizationResult', or 'OrganizationError' object. + """ result: RemoveOrganizationUnion! clientMutationId: String } input RemoveOrganizationInput { - # The global id of the organization you wish you remove. + """ + The global id of the organization you wish you remove. + """ orgId: ID! clientMutationId: String } type UpdateOrganizationPayload { - # 'UpdateOrganizationUnion' returning either an 'Organization', or 'OrganizationError' object. + """ + 'UpdateOrganizationUnion' returning either an 'Organization', or 'OrganizationError' object. + """ result: UpdateOrganizationUnion! clientMutationId: String } - # This union is used with the 'UpdateOrganization' mutation, - # allowing for users to update an organization, and support any errors that may occur + """ + This union is used with the 'UpdateOrganization' mutation, + allowing for users to update an organization, and support any errors that may occur + """ union UpdateOrganizationUnion = OrganizationError | Organization input UpdateOrganizationInput { - # The global id of the organization to be updated. + """ + The global id of the organization to be updated. + """ id: ID! - # The English acronym of the organization. + """ + The English acronym of the organization. + """ acronymEN: Acronym - # The French acronym of the organization. + """ + The French acronym of the organization. + """ acronymFR: Acronym - # The English name of the organization. + """ + The English name of the organization. + """ nameEN: String - # The French name of the organization. + """ + The French name of the organization. + """ nameFR: String - # The English translation of the zone the organization belongs to. + """ + The English translation of the zone the organization belongs to. + """ zoneEN: String - # The English translation of the zone the organization belongs to. + """ + The English translation of the zone the organization belongs to. + """ zoneFR: String - # The English translation of the sector the organization belongs to. + """ + The English translation of the sector the organization belongs to. + """ sectorEN: String - # The French translation of the sector the organization belongs to. + """ + The French translation of the sector the organization belongs to. + """ sectorFR: String - # The English translation of the country the organization resides in. + """ + The English translation of the country the organization resides in. + """ countryEN: String - # The French translation of the country the organization resides in. + """ + The French translation of the country the organization resides in. + """ countryFR: String - # The English translation of the province the organization resides in. + """ + The English translation of the province the organization resides in. + """ provinceEN: String - # The French translation of the province the organization resides in. + """ + The French translation of the province the organization resides in. + """ provinceFR: String - # The English translation of the city the organization resides in. + """ + The English translation of the city the organization resides in. + """ cityEN: String - # The French translation of the city the organization resides in. + """ + The French translation of the city the organization resides in. + """ cityFR: String + + """ + If the organization has domains that are managed externally. + """ + externallyManaged: Boolean clientMutationId: String } type VerifyOrganizationPayload { - # 'VerifyOrganizationUnion' returning either an 'OrganizationResult', or 'OrganizationError' object. + """ + 'VerifyOrganizationUnion' returning either an 'OrganizationResult', or 'OrganizationError' object. + """ result: VerifyOrganizationUnion clientMutationId: String } - # This union is used with the 'VerifyOrganization' mutation, - # allowing for super admins to verify an organization, - # and support any errors that may occur + """ + This union is used with the 'VerifyOrganization' mutation, + allowing for super admins to verify an organization, + and support any errors that may occur + """ union VerifyOrganizationUnion = OrganizationError | OrganizationResult input VerifyOrganizationInput { - # The global id of the organization to be verified. + """ + The global id of the organization to be verified. + """ orgId: ID! clientMutationId: String } type AuthenticatePayload { - # Authenticate union returning either a 'authResult' or 'authenticateError' object. + """ + Authenticate union returning either a 'authResult' or 'authenticateError' object. + """ result: AuthenticateUnion clientMutationId: String } - # This union is used with the 'authenticate' mutation, allowing for the user to authenticate, and support any errors that may occur + """ + This union is used with the 'authenticate' mutation, allowing for the user to authenticate, and support any errors that may occur + """ union AuthenticateUnion = AuthResult | AuthenticateError - # An object used to return information when users sign up or authenticate. + """ + An object used to return information when users sign up or authenticate. + """ type AuthResult { - # JWT used for accessing controlled content. + """ + JWT used for accessing controlled content. + """ authToken: String - # User that has just been created or signed in. + """ + User that has just been created or signed in. + """ user: PersonalUser } - # This object is used to inform the user if any errors occurred during authentication. + """ + This object is used to inform the user if any errors occurred during authentication. + """ type AuthenticateError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } input AuthenticateInput { - # The method that the user wants to receive their authentication code by. + """ + The method that the user wants to receive their authentication code by. + """ sendMethod: TFASendMethodEnum! - # Security code found in text msg, or email inbox. + """ + Security code found in text msg, or email inbox. + """ authenticationCode: Int! - # The JWT that is retrieved from the sign in mutation. + """ + The JWT that is retrieved from the sign in mutation. + """ authenticateToken: String! clientMutationId: String } type CloseAccountOtherPayload { - # 'CloseAccountUnion' returning either a 'CloseAccountResult', or 'CloseAccountError' object. + """ + 'CloseAccountUnion' returning either a 'CloseAccountResult', or 'CloseAccountError' object. + """ result: CloseAccountUnion clientMutationId: String } - # This union is used for the 'closeAccount' mutation, to support successful or errors that may occur. + """ + This union is used for the 'closeAccount' mutation, to support successful or errors that may occur. + """ union CloseAccountUnion = CloseAccountResult | CloseAccountError - # This object is used to inform the user of the status of closing their account. + """ + This object is used to inform the user of the status of closing their account. + """ type CloseAccountResult { - # Status of closing the users account. + """ + Status of closing the users account. + """ status: String } - # This object is used to inform the user if any errors occurred while closing their account. + """ + This object is used to inform the user if any errors occurred while closing their account. + """ type CloseAccountError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue encountered. + """ + Description of the issue encountered. + """ description: String } input CloseAccountOtherInput { - # The user id of a user you want to close the account of. + """ + The user id of a user you want to close the account of. + """ userId: ID clientMutationId: String } type CloseAccountSelfPayload { - # 'CloseAccountUnion' returning either a 'CloseAccountResult', or 'CloseAccountError' object. + """ + 'CloseAccountUnion' returning either a 'CloseAccountResult', or 'CloseAccountError' object. + """ result: CloseAccountUnion clientMutationId: String } @@ -3579,12 +6079,16 @@ export const getTypeNames = () => gql` } type RefreshTokensPayload { - # Refresh tokens union returning either a 'authResult' or 'authenticateError' object. + """ + Refresh tokens union returning either a 'authResult' or 'authenticateError' object. + """ result: RefreshTokensUnion clientMutationId: String } - # This union is used with the 'refreshTokens' mutation, allowing for the user to refresh their tokens, and support any errors that may occur + """ + This union is used with the 'refreshTokens' mutation, allowing for the user to refresh their tokens, and support any errors that may occur + """ union RefreshTokensUnion = AuthResult | AuthenticateError input RefreshTokensInput { @@ -3592,26 +6096,40 @@ export const getTypeNames = () => gql` } type RemovePhoneNumberPayload { - # 'RemovePhoneNumberUnion' returning either a 'RemovePhoneNumberResult', or 'RemovePhoneNumberError' object. + """ + 'RemovePhoneNumberUnion' returning either a 'RemovePhoneNumberResult', or 'RemovePhoneNumberError' object. + """ result: RemovePhoneNumberUnion clientMutationId: String } - # This union is used with the 'RemovePhoneNumber' mutation, allowing for users to remove their phone number, and support any errors that may occur + """ + This union is used with the 'RemovePhoneNumber' mutation, allowing for users to remove their phone number, and support any errors that may occur + """ union RemovePhoneNumberUnion = RemovePhoneNumberError | RemovePhoneNumberResult - # This object is used to inform the user if any errors occurred while removing their phone number. + """ + This object is used to inform the user if any errors occurred while removing their phone number. + """ type RemovePhoneNumberError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while removing their phone number. + """ + This object is used to inform the user that no errors were encountered while removing their phone number. + """ type RemovePhoneNumberResult { - # Informs the user if the phone number removal was successful. + """ + Informs the user if the phone number removal was successful. + """ status: String } @@ -3620,139 +6138,209 @@ export const getTypeNames = () => gql` } type ResetPasswordPayload { - # 'ResetPasswordUnion' returning either a 'ResetPasswordResult', or 'ResetPasswordError' object. + """ + 'ResetPasswordUnion' returning either a 'ResetPasswordResult', or 'ResetPasswordError' object. + """ result: ResetPasswordUnion clientMutationId: String } - # This union is used with the 'ResetPassword' mutation, allowing for users to reset their password, and support any errors that may occur + """ + This union is used with the 'ResetPassword' mutation, allowing for users to reset their password, and support any errors that may occur + """ union ResetPasswordUnion = ResetPasswordError | ResetPasswordResult - # This object is used to inform the user if any errors occurred while resetting their password. + """ + This object is used to inform the user if any errors occurred while resetting their password. + """ type ResetPasswordError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while resetting their password. + """ + This object is used to inform the user that no errors were encountered while resetting their password. + """ type ResetPasswordResult { - # Informs the user if the password reset was successful, and to redirect to sign in page. + """ + Informs the user if the password reset was successful, and to redirect to sign in page. + """ status: String } input ResetPasswordInput { - # The users new password. + """ + The users new password. + """ password: String! - # A confirmation password to confirm the new password. + """ + A confirmation password to confirm the new password. + """ confirmPassword: String! - # The JWT found in the url, redirected from the email they received. + """ + The JWT found in the url, redirected from the email they received. + """ resetToken: String! clientMutationId: String } type SendEmailVerificationPayload { - # Informs the user if the email was sent successfully. + """ + Informs the user if the email was sent successfully. + """ status: String clientMutationId: String } input SendEmailVerificationInput { - # The users email address used for sending the verification email. + """ + The users email address used for sending the verification email. + """ userName: EmailAddress! clientMutationId: String } type SendPasswordResetLinkPayload { - # Informs the user if the password reset email was sent successfully. + """ + Informs the user if the password reset email was sent successfully. + """ status: String clientMutationId: String } input SendPasswordResetLinkInput { - # User name for the account you would like to receive a password reset link for. + """ + User name for the account you would like to receive a password reset link for. + """ userName: EmailAddress! clientMutationId: String } type SetPhoneNumberPayload { - # 'SetPhoneNumberUnion' returning either a 'SetPhoneNumberResult', or 'SetPhoneNumberError' object. + """ + 'SetPhoneNumberUnion' returning either a 'SetPhoneNumberResult', or 'SetPhoneNumberError' object. + """ result: SetPhoneNumberUnion clientMutationId: String } - # This union is used with the 'setPhoneNumber' mutation, allowing for users to send a verification code to their phone, and support any errors that may occur + """ + This union is used with the 'setPhoneNumber' mutation, allowing for users to send a verification code to their phone, and support any errors that may occur + """ union SetPhoneNumberUnion = SetPhoneNumberError | SetPhoneNumberResult - # This object is used to inform the user if any errors occurred while setting a new phone number. + """ + This object is used to inform the user if any errors occurred while setting a new phone number. + """ type SetPhoneNumberError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while setting a new phone number. + """ + This object is used to inform the user that no errors were encountered while setting a new phone number. + """ type SetPhoneNumberResult { - # Informs the user if their phone code was successfully sent. + """ + Informs the user if their phone code was successfully sent. + """ status: String - # The user who set their phone number. + """ + The user who set their phone number. + """ user: PersonalUser } input SetPhoneNumberInput { - # The phone number that the text message will be sent to. + """ + The phone number that the text message will be sent to. + """ phoneNumber: PhoneNumber! clientMutationId: String } type SignInPayload { - # 'SignInUnion' returning either a 'regularSignInResult', 'tfaSignInResult', or 'signInError' object. + """ + 'SignInUnion' returning either a 'regularSignInResult', 'tfaSignInResult', or 'signInError' object. + """ result: SignInUnion clientMutationId: String } - # This union is used with the 'SignIn' mutation, allowing for multiple styles of logging in, and support any errors that may occur + """ + This union is used with the 'SignIn' mutation, allowing for multiple styles of logging in, and support any errors that may occur + """ union SignInUnion = AuthResult | SignInError | TFASignInResult - # This object is used to inform the user if any errors occurred during sign in. + """ + This object is used to inform the user if any errors occurred during sign in. + """ type SignInError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used when the user signs in and has validated either their email or phone. + """ + This object is used when the user signs in and has validated either their email or phone. + """ type TFASignInResult { - # Token used to verify during authentication. + """ + Token used to verify during authentication. + """ authenticateToken: String - # Whether the authentication code was sent through text, or email. + """ + Whether the authentication code was sent through text, or email. + """ sendMethod: String } input SignInInput { - # The email the user signed up with. + """ + The email the user signed up with. + """ userName: EmailAddress! - # The password the user signed up with + """ + The password the user signed up with + """ password: String! - # Whether or not the user wants to stay signed in after leaving the site. + """ + Whether or not the user wants to stay signed in after leaving the site. + """ rememberMe: Boolean = false clientMutationId: String } type SignOutPayload { - # Status of the users signing-out. + """ + Status of the users signing-out. + """ status: String clientMutationId: String } @@ -3762,190 +6350,296 @@ export const getTypeNames = () => gql` } type SignUpPayload { - # 'SignUpUnion' returning either a 'AuthResult', or 'SignUpError' object. + """ + 'SignUpUnion' returning either a 'TFASignInResult', or 'SignUpError' object. + """ result: SignUpUnion clientMutationId: String } - # This union is used with the 'signUp' mutation, allowing for the user to sign up, and support any errors that may occur. + """ + This union is used with the 'signUp' mutation, allowing for the user to sign up, and support any errors that may occur. + """ union SignUpUnion = TFASignInResult | SignUpError - # This object is used to inform the user if any errors occurred during sign up. + """ + This object is used to inform the user if any errors occurred during sign up. + """ type SignUpError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } input SignUpInput { - # The name that will be displayed to other users. + """ + The name that will be displayed to other users. + """ displayName: String! - # Email address that the user will use to authenticate with. + """ + Email address that the user will use to authenticate with. + """ userName: EmailAddress! - # The password the user will authenticate with. + """ + The password the user will authenticate with. + """ password: String! - # A secondary password field used to confirm the user entered the correct password. + """ + A secondary password field used to confirm the user entered the correct password. + """ confirmPassword: String! - # The users preferred language. + """ + The users preferred language. + """ preferredLang: LanguageEnums! - # A token sent by email, that will assign a user to an organization with a pre-determined role. + """ + A token sent by email, that will assign a user to an organization with a pre-determined role. + """ signUpToken: String - # Whether or not the user wants to stay signed in after leaving the site. + """ + Whether or not the user wants to stay signed in after leaving the site. + """ rememberMe: Boolean = false clientMutationId: String } type UpdateUserPasswordPayload { - # 'UpdateUserPasswordUnion' returning either a 'UpdateUserPasswordResultType', or 'UpdateUserPasswordError' object. + """ + 'UpdateUserPasswordUnion' returning either a 'UpdateUserPasswordResultType', or 'UpdateUserPasswordError' object. + """ result: UpdateUserPasswordUnion clientMutationId: String } - # This union is used with the 'updateUserPassword' mutation, allowing for users to update their password, and support any errors that may occur + """ + This union is used with the 'updateUserPassword' mutation, allowing for users to update their password, and support any errors that may occur + """ union UpdateUserPasswordUnion = UpdateUserPasswordError | UpdateUserPasswordResultType - # This object is used to inform the user if any errors occurred while updating their password. + """ + This object is used to inform the user if any errors occurred while updating their password. + """ type UpdateUserPasswordError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while updating their password. + """ + This object is used to inform the user that no errors were encountered while updating their password. + """ type UpdateUserPasswordResultType { - # Informs the user if their password was successfully updated. + """ + Informs the user if their password was successfully updated. + """ status: String } input UpdateUserPasswordInput { - # The users current password to verify it is the current user. + """ + The users current password to verify it is the current user. + """ currentPassword: String! - # The new password the user wishes to change to. + """ + The new password the user wishes to change to. + """ updatedPassword: String! - # A password confirmation of their new password. + """ + A password confirmation of their new password. + """ updatedPasswordConfirm: String! clientMutationId: String } type UpdateUserProfilePayload { - # 'UpdateUserProfileUnion' returning either a 'UpdateUserProfileResult', or 'UpdateUserProfileError' object. + """ + 'UpdateUserProfileUnion' returning either a 'UpdateUserProfileResult', or 'UpdateUserProfileError' object. + """ result: UpdateUserProfileUnion clientMutationId: String } - # This union is used with the 'updateUserProfile' mutation, allowing for users to update their profile, and support any errors that may occur + """ + This union is used with the 'updateUserProfile' mutation, allowing for users to update their profile, and support any errors that may occur + """ union UpdateUserProfileUnion = UpdateUserProfileError | UpdateUserProfileResult - # This object is used to inform the user if any errors occurred while updating their profile. + """ + This object is used to inform the user if any errors occurred while updating their profile. + """ type UpdateUserProfileError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while resetting their password. + """ + This object is used to inform the user that no errors were encountered while resetting their password. + """ type UpdateUserProfileResult { - # Informs the user if the password reset was successful, and to redirect to sign in page. + """ + Informs the user if the password reset was successful, and to redirect to sign in page. + """ status: String - # Return the newly updated user information. + """ + Return the newly updated user information. + """ user: PersonalUser } input UpdateUserProfileInput { - # The updated display name the user wishes to change to. + """ + The updated display name the user wishes to change to. + """ displayName: String - # The updated user name the user wishes to change to. + """ + The updated user name the user wishes to change to. + """ userName: EmailAddress - # The updated preferred language the user wishes to change to. + """ + The updated preferred language the user wishes to change to. + """ preferredLang: LanguageEnums - # The method in which the user wishes to have their TFA code sent via. + """ + The method in which the user wishes to have their TFA code sent via. + """ tfaSendMethod: TFASendMethodEnum - # The updated boolean which represents if the user wants to see features in progress. + """ + The updated boolean which represents if the user wants to see features in progress. + """ insideUser: Boolean - # The updated boolean which represents if the user wants to receive update emails. + """ + The updated boolean which represents if the user wants to receive update emails. + """ receiveUpdateEmails: Boolean clientMutationId: String } type VerifyAccountPayload { - # 'VerifyAccountUnion' returning either a 'VerifyAccountResult', or 'VerifyAccountError' object. + """ + 'VerifyAccountUnion' returning either a 'VerifyAccountResult', or 'VerifyAccountError' object. + """ result: VerifyAccountUnion clientMutationId: String } - # This union is used with the 'verifyAccount' mutation, allowing for users to verify their account, and support any errors that may occur + """ + This union is used with the 'verifyAccount' mutation, allowing for users to verify their account, and support any errors that may occur + """ union VerifyAccountUnion = VerifyAccountError | VerifyAccountResult - # This object is used to inform the user if any errors occurred while verifying their account. + """ + This object is used to inform the user if any errors occurred while verifying their account. + """ type VerifyAccountError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while verifying their account. + """ + This object is used to inform the user that no errors were encountered while verifying their account. + """ type VerifyAccountResult { - # Informs the user if their account was successfully verified. + """ + Informs the user if their account was successfully verified. + """ status: String } input VerifyAccountInput { - # Token sent via email, and located in url. + """ + Token sent via email, and located in url. + """ verifyTokenString: String! clientMutationId: String } type verifyPhoneNumberPayload { - # 'VerifyPhoneNumberUnion' returning either a 'VerifyPhoneNumberResult', or 'VerifyPhoneNumberError' object. + """ + 'VerifyPhoneNumberUnion' returning either a 'VerifyPhoneNumberResult', or 'VerifyPhoneNumberError' object. + """ result: VerifyPhoneNumberUnion clientMutationId: String } - # This union is used with the 'verifyPhoneNumber' mutation, allowing for users to verify their phone number, and support any errors that may occur + """ + This union is used with the 'verifyPhoneNumber' mutation, allowing for users to verify their phone number, and support any errors that may occur + """ union VerifyPhoneNumberUnion = VerifyPhoneNumberError | VerifyPhoneNumberResult - # This object is used to inform the user if any errors occurred while verifying their phone number. + """ + This object is used to inform the user if any errors occurred while verifying their phone number. + """ type VerifyPhoneNumberError { - # Error code to inform user what the issue is related to. + """ + Error code to inform user what the issue is related to. + """ code: Int - # Description of the issue that was encountered. + """ + Description of the issue that was encountered. + """ description: String } - # This object is used to inform the user that no errors were encountered while verifying their phone number. + """ + This object is used to inform the user that no errors were encountered while verifying their phone number. + """ type VerifyPhoneNumberResult { - # Informs the user if their phone number was successfully verified. + """ + Informs the user if their phone number was successfully verified. + """ status: String - # The user who verified their phone number. + """ + The user who verified their phone number. + """ user: PersonalUser } input verifyPhoneNumberInput { - # The two factor code that was received via text message. + """ + The two factor code that was received via text message. + """ twoFactorCode: Int! clientMutationId: String } diff --git a/frontend/mocking/mocker.js b/frontend/mocking/mocker.js index 657b43f03..b201de249 100644 --- a/frontend/mocking/mocker.js +++ b/frontend/mocking/mocker.js @@ -55,7 +55,6 @@ const mockOverrides = { totalCount: affiliationCount, }, emailValidated: true, - preferredLang: 'ENGLISH', } }, SignInUnion: () => ({ __typename: 'AuthResult' }), @@ -479,6 +478,13 @@ const mocks = { totalCount: numberOfEdges, } }, + OrganizationSummaryConnection: () => { + const numberOfEdges = faker.datatype.number({ min: 30, max: 365 }) + return { + edges: [...new Array(numberOfEdges)], + totalCount: numberOfEdges, + } + }, PersonalUser: () => { const affiliationCount = faker.datatype.number({ min: 0, max: 200 }) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0543b9c91..7d9a125a4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,40 +10,48 @@ "license": "MIT", "dependencies": { "@apollo/client": "^3.6.9", - "@babel/runtime": "^7.18.6", - "@chakra-ui/icons": "^1.1.7", - "@chakra-ui/react": "^1.8.8", - "@chakra-ui/system": "^1.0.0", + "@babel/runtime": "^7.27.0", + "@chakra-ui/icons": "^2.1.1", + "@chakra-ui/react": "^2.8.2", + "@chakra-ui/system": "^2.6.2", "@emotion/react": "^11.9.3", "@emotion/styled": "^11.9.3", - "@lingui/react": "^3.14.0", + "@lingui/react": "^5.9.3", "@visx/axis": "^2.17.0", - "@visx/event": "^2.6.0", - "@visx/grid": "^2.17.0", + "@visx/curve": "^3.3.0", + "@visx/event": "^2.17.0", + "@visx/glyph": "^3.3.0", + "@visx/gradient": "^3.3.0", + "@visx/grid": "^3.5.0", "@visx/group": "^2.1.0", "@visx/legend": "^2.17.0", - "@visx/responsive": "^2.10.0", - "@visx/scale": "^2.17.0", - "@visx/shape": "^2.17.0", - "@visx/tooltip": "^2.10.0", - "body-parser": "^1.19.1", + "@visx/mock-data": "^3.3.0", + "@visx/responsive": "^2.17.0", + "@visx/scale": "^3.5.0", + "@visx/shape": "^3.5.0", + "@visx/tooltip": "^2.17.0", + "@visx/vendor": "^3.5.0", + "body-parser": "^1.20.5", + "compression": "^1.8.1", "d3": "^7.8.0", "dotenv": "^16.0.1", - "express": "^4.17.2", + "express": "^4.22.2", "formik": "^2.2.9", "framer-motion": "^4.1.17", + "intro.js": "^7.2.0", + "intro.js-react": "^1.0.0", "isomorphic-unfetch": "^3.1.0", - "json-2-csv": "^3.17.1", + "json-2-csv": "^5.5.11", + "lodash-es": "^4.18.1", "make-plural": "^7.1.0", "prop-types": "^15.8.1", "react": "^18.2.0", - "react-diff-viewer-continued": "^3.2.6", "react-dom": "^18.2.0", "react-error-boundary": "^3.1.4", + "react-joyride": "^2.8.2", "react-phone-input-2": "^2.15.1", - "react-router-dom": "^5.3.3", + "react-router-dom": "^6.30.4", "react-table": "^7.8.0", - "subscriptions-transport-ws": "^0.11.0", "yup": "^0.32.11" }, "devDependencies": { @@ -55,9 +63,10 @@ "@babel/preset-react": "^7.18.6", "@graphql-tools/mock": "^8.7.0", "@graphql-tools/schema": "^10.0.0", - "@lingui/cli": "^3.14.0", - "@lingui/loader": "^3.14.0", - "@lingui/macro": "^3.14.0", + "@lingui/cli": "^5.9.3", + "@lingui/core": "^5.9.3", + "@lingui/loader": "^5.9.3", + "@lingui/macro": "^5.9.3", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.4.0", "@testing-library/react-hooks": "^7.0.2", @@ -67,6 +76,7 @@ "babel-loader": "^8.2.5", "babel-plugin-macros": "^3.1.0", "clean-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^14.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", @@ -88,10 +98,10 @@ "react-test-renderer": "^18.2.0", "source-map-loader": "^4.0.0", "supertest": "^6.2.3", - "webpack": "^5.76.2", - "webpack-cli": "^4.10.0", + "webpack": "^5.105.0", + "webpack-cli": "^5.1.4", "webpack-config-utils": "^2.3.1", - "webpack-dev-server": "^4.9.2" + "webpack-dev-server": "^5.2.5" } }, "node_modules/@adobe/css-tools": { @@ -387,47 +397,48 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "devOptional": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "devOptional": true, "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", - "convert-source-map": "^1.7.0", + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -437,16 +448,27 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "devOptional": true + }, "node_modules/@babel/core/node_modules/debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "devOptional": true, "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/@babel/core/node_modules/ms": { @@ -465,32 +487,27 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "devOptional": true, + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "devOptional": true, + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@babel/helper-annotate-as-pure": { @@ -519,21 +536,28 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "devOptional": true, "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "devOptional": true, + "dependencies": { + "yallist": "^3.0.2" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { @@ -545,6 +569,12 @@ "semver": "bin/semver.js" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "devOptional": true + }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", @@ -635,7 +665,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6.9.0" } @@ -656,7 +686,7 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" @@ -665,14 +695,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "devOptional": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "engines": { "node": ">=6.9.0" } @@ -690,33 +716,32 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "devOptional": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { @@ -732,9 +757,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "engines": { "node": ">=6.9.0" @@ -775,12 +800,13 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", - "devOptional": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -802,7 +828,7 @@ "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -811,25 +837,25 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "devOptional": true, "engines": { "node": ">=6.9.0" @@ -851,37 +877,40 @@ } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "devOptional": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "devOptional": true, + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dependencies": { + "@babel/types": "^7.29.0" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1630,16 +1659,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -2108,77 +2136,75 @@ } }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "devOptional": true, + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "devOptional": true, + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "devOptional": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/@babel/traverse/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2191,941 +2217,1342 @@ "dev": true }, "node_modules/@chakra-ui/accordion": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/accordion/-/accordion-1.4.11.tgz", - "integrity": "sha512-d/gvSgGwcZaJXxXqGmecpAgko/tUYb5vR0E0B2/V/z9AVbS8ei//fbiO9+8Ouyl/K46oWHWYj5vt8iTadlZleg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/accordion/-/accordion-2.3.1.tgz", + "integrity": "sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==", "dependencies": { - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/transition": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "framer-motion": ">=4.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/alert": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/alert/-/alert-1.3.7.tgz", - "integrity": "sha512-fFpJYBpHOIK/BX4BVl/xafYiDBUW+Bq/gUYDOo4iAiO4vHgxo74oa+yOwSRNlNjAgIX7pi2ridsYQALKyWyxxQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/alert/-/alert-2.2.2.tgz", + "integrity": "sha512-jHg4LYMRNOJH830ViLuicjb3F+v6iriE/2G5T+Sd0Hna04nukNJ1MxUmBPE+vI22me2dIflfelu2v9wdB6Pojw==", "dependencies": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/spinner": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/anatomy": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-1.3.0.tgz", - "integrity": "sha512-vj/lcHkCuq/dtbl69DkNsftZTnrGEegB90ODs1B6rxw8iVMdDSYkthPPFAkqzNs4ppv1y2IBjELuVzpeta1OHA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz", + "integrity": "sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==" + }, + "node_modules/@chakra-ui/avatar": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/avatar/-/avatar-2.3.0.tgz", + "integrity": "sha512-8gKSyLfygnaotbJbDMHDiJoF38OHXUYVme4gGxZ1fLnQEdPVEaIWfH+NndIjOM0z8S+YEFnT9KyGMUtvPrBk3g==", "dependencies": { - "@chakra-ui/theme-tools": "^1.3.6" + "@chakra-ui/image": "2.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/avatar": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/avatar/-/avatar-1.3.11.tgz", - "integrity": "sha512-/eRRK48Er92/QWAfWhxsJIN0gZBBvk+ew4Hglo+pxt3/NDnfTF2yPE7ZN29Dl6daPNbyTOpoksMwaU2mZIqLgA==", + "node_modules/@chakra-ui/breadcrumb": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/breadcrumb/-/breadcrumb-2.2.0.tgz", + "integrity": "sha512-4cWCG24flYBxjruRi4RJREWTGF74L/KzI2CognAW/d/zWR0CjiScuJhf37Am3LFbCySP6WSoyBOtTIoTA4yLEA==", "dependencies": { - "@chakra-ui/image": "1.1.10", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/breadcrumb": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/breadcrumb/-/breadcrumb-1.3.6.tgz", - "integrity": "sha512-iXxienBO6RUnJEcDvyDWyRt+mzPyl7/b6N8i0vrjGKGLpgtayJFvIdo33tFcvx6TCy7V9hiE3HTtZnNomWdR6A==", + "node_modules/@chakra-ui/breakpoint-utils": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.8.tgz", + "integrity": "sha512-Pq32MlEX9fwb5j5xx8s18zJMARNHlQZH2VH1RZgfgRDpp7DcEgtRW5AInfN5CfqdHLO1dGxA7I3MqEuL5JnIsA==", + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5" + } + }, + "node_modules/@chakra-ui/button": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/button/-/button-2.1.0.tgz", + "integrity": "sha512-95CplwlRKmmUXkdEp/21VkEWgnwcx2TOBG6NfYlsuLBDHSLlo5FKIiE2oSi4zXc4TLcopGcWPNcm/NDaSC5pvA==", "dependencies": { - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/spinner": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/button": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/button/-/button-1.5.10.tgz", - "integrity": "sha512-IVEOrleI378CckAa3b3CTUHMPZRfpy6LPwn1Mx3sMpHEkDTKu8zJcjgEvCE8HYzNC1KbwBsa1PfTgk40ui6EtA==", + "node_modules/@chakra-ui/card": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/card/-/card-2.2.0.tgz", + "integrity": "sha512-xUB/k5MURj4CtPAhdSoXZidUbm8j3hci9vnc+eZJVDqhDOShNlD6QeniQNRPRys4lWAQLCbFcrwL29C8naDi6g==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/spinner": "1.2.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/checkbox": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/checkbox/-/checkbox-1.7.1.tgz", - "integrity": "sha512-9Io97yn8OrdaIynCj+3Z/neJV7lTT1MtcdYh3BKMd7WnoJDkRY/GlBM8zsdgC5Wvm+ZQ1M83t0YvRPKLLzusyA==", - "dependencies": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/checkbox/-/checkbox-2.3.2.tgz", + "integrity": "sha512-85g38JIXMEv6M+AcyIGLh7igNtfpAN6KGQFYxY9tBj0eWvWk4NKQxvqqyVta0bSAyIl1rixNIIezNpNWk2iO4g==", + "dependencies": { + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/visually-hidden": "2.2.0", + "@zag-js/focus-visible": "0.16.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/clickable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/clickable/-/clickable-1.2.6.tgz", - "integrity": "sha512-89SsrQwwwAadcl/bN8nZqqaaVhVNFdBXqQnxVy1t07DL5ezubmNb5SgFh9LDznkm9YYPQhaGr3W6HFro7iAHMg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/clickable/-/clickable-2.1.0.tgz", + "integrity": "sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw==", "dependencies": { - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, "node_modules/@chakra-ui/close-button": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/close-button/-/close-button-1.2.7.tgz", - "integrity": "sha512-cYTxfgrIlPU4IZm1sehZXxx/TNQBk9c3LBPvTpywEM8GVRGINh4YLq8WiMaPtO+TDNBnKoWS/jS4IHnR+abADw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/close-button/-/close-button-2.1.1.tgz", + "integrity": "sha512-gnpENKOanKexswSVpVz7ojZEALl2x5qjLYNqSQGbxz+aP9sOXPfUS56ebyBrre7T7exuWGiFeRwnM0oVeGPaiw==", "dependencies": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/icon": "3.2.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/color-mode": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-1.4.8.tgz", - "integrity": "sha512-iD4126DVQi06c6ARr3uf3R2rtEu8aBVjW8rhZ+lOsV26Z15iCJA7OAut13Xu06fcZvgjSB/ChDy6Sx9sV9UjHA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz", + "integrity": "sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, "node_modules/@chakra-ui/control-box": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/control-box/-/control-box-1.1.6.tgz", - "integrity": "sha512-EUcq5f854puG6ZA6wAWl4107OPl8+bj4MMHJCa48BB0qec0U8HCEtxQGnFwJmaYLalIAjMfHuY3OwO2A3Hi9hA==", - "dependencies": { - "@chakra-ui/utils": "1.10.4" - }, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/control-box/-/control-box-2.1.0.tgz", + "integrity": "sha512-gVrRDyXFdMd8E7rulL0SKeoljkLQiPITFnsyMO8EFHNZ+AHt5wK4LIguYVEq88APqAGZGfHFWXr79RYrNiE3Mg==", "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/counter": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-1.2.10.tgz", - "integrity": "sha512-HQd09IuJ4z8M8vWajH+99jBWWSHDesQZmnN95jUg3HKOuNleLaipf2JFdrqbO1uWQyHobn2PM6u+B+JCAh2nig==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-2.1.0.tgz", + "integrity": "sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/number-utils": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, "node_modules/@chakra-ui/css-reset": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.3.tgz", - "integrity": "sha512-AgfrE7bRTJvNi/4zIfacI/kBHmHmHEIeQtHwCvk/0qM9V2gK1VM3ctYlnibf7BTh17F/UszweOGRb1lHSPfWjw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-2.3.0.tgz", + "integrity": "sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==", "peerDependencies": { "@emotion/react": ">=10.0.35", - "react": ">=16.8.6" + "react": ">=18" } }, "node_modules/@chakra-ui/descendant": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/descendant/-/descendant-2.1.3.tgz", - "integrity": "sha512-aNYNv99gEPENCdw2N5y3FvL5wgBVcLiOzJ2TxSwb4EVYszbgBZ8Ry1pf7lkoSfysdxD0scgy2cVyxO8TsYTU4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/descendant/-/descendant-3.1.0.tgz", + "integrity": "sha512-VxCIAir08g5w27klLyi7PVo8BxhW4tgU/lxQyujkmi4zx7hT9ZdrcQLAted/dAa+aSIZ14S1oV0Q9lGjsAdxUQ==", "dependencies": { - "@chakra-ui/react-utils": "^1.2.3" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, + "node_modules/@chakra-ui/dom-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/dom-utils/-/dom-utils-2.1.0.tgz", + "integrity": "sha512-ZmF2qRa1QZ0CMLU8M1zCfmw29DmPNtfjR9iTo74U5FPr3i1aoAh7fbJ4qAlZ197Xw9eAW28tvzQuoVWeL5C7fQ==" + }, "node_modules/@chakra-ui/editable": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@chakra-ui/editable/-/editable-1.4.2.tgz", - "integrity": "sha512-a5zKghA/IvG7yNkmFl7Z9c2KSsf0FgyijsNPTg/4S5jxyz13QJtoTg40tdpyaxHHCT25y25iUcV4FYCj6Jd01w==", - "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/editable/-/editable-3.1.0.tgz", + "integrity": "sha512-j2JLrUL9wgg4YA6jLlbU88370eCRyor7DZQD9lzpY95tSOXpTljeg3uF9eOmDnCs6fxp3zDWIfkgMm/ExhcGTg==", + "dependencies": { + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, + "node_modules/@chakra-ui/event-utils": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@chakra-ui/event-utils/-/event-utils-2.0.8.tgz", + "integrity": "sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw==" + }, "node_modules/@chakra-ui/focus-lock": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/focus-lock/-/focus-lock-1.2.6.tgz", - "integrity": "sha512-ZJNE1oNdUM1aGWuCJ+bxFa/d3EwxzfMWzTKzSvKDK50GWoUQQ10xFTT9nY/yFpkcwhBvx1KavxKf44mIhIbSog==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/focus-lock/-/focus-lock-2.1.0.tgz", + "integrity": "sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==", "dependencies": { - "@chakra-ui/utils": "1.10.4", - "react-focus-lock": "2.5.2" + "@chakra-ui/dom-utils": "2.1.0", + "react-focus-lock": "^2.9.4" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/focus-lock/node_modules/react-focus-lock": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.5.2.tgz", - "integrity": "sha512-WzpdOnEqjf+/A3EH9opMZWauag7gV0BxFl+EY4ElA4qFqYsUsBLnmo2sELbN5OC30S16GAWMy16B9DLPpdJKAQ==", + "node_modules/@chakra-ui/form-control": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-2.2.0.tgz", + "integrity": "sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw==", "dependencies": { - "@babel/runtime": "^7.0.0", - "focus-lock": "^0.9.1", - "prop-types": "^15.6.2", - "react-clientside-effect": "^1.2.5", - "use-callback-ref": "^1.2.5", - "use-sidecar": "^1.0.5" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/form-control": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-1.6.0.tgz", - "integrity": "sha512-MtUE98aocP2QTgvyyJ/ABuG33mhT3Ox56phKreG3HzbUKByMwrbQSm1QcAgyYdqSZ9eKB2tXx+qgGNh+avAfDA==", + "node_modules/@chakra-ui/hooks": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-2.2.1.tgz", + "integrity": "sha512-RQbTnzl6b1tBjbDPf9zGRo9rf/pQMholsOudTxjy4i9GfTfz6kgp5ValGjQm2z7ng6Z31N1cnjZ1AlSzQ//ZfQ==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-utils": "2.0.12", + "@chakra-ui/utils": "2.0.15", + "compute-scroll-into-view": "3.0.3", + "copy-to-clipboard": "3.3.3" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/hooks": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-1.9.1.tgz", - "integrity": "sha512-SEeh1alDKzrP9gMLWMnXOUDBQDKF/URL6iTmkumTn6vhawWNla6sPrcMyoCzWdMzwUhZp3QNtCKbUm7dxBXvPw==", + "node_modules/@chakra-ui/hooks/node_modules/@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", "dependencies": { - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "compute-scroll-into-view": "1.0.14", - "copy-to-clipboard": "3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.6" + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" } }, + "node_modules/@chakra-ui/hooks/node_modules/@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@chakra-ui/hooks/node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/@chakra-ui/hooks/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/@chakra-ui/icon": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-2.0.5.tgz", - "integrity": "sha512-ZrqRvCCIxGr4qFd/r1pmtd9tobRmv8KAxV7ygFoc/t4vOSKTcVIjhE12gsI3FzgvXM15ZFVwsxa1zodwgo5neQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-3.2.0.tgz", + "integrity": "sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==", "dependencies": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/icons": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-1.1.7.tgz", - "integrity": "sha512-YIHxey/B4M2PyFASlHXtAWFyW+tsAtGAChOJ8dsM2kpu1MbVUqm/6nMI1KIFd7Te5IWuNYA75rAHBdLI0Yu61A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.1.1.tgz", + "integrity": "sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g==", "dependencies": { - "@chakra-ui/icon": "2.0.5", - "@types/react": "^17.0.15" + "@chakra-ui/icon": "3.2.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/image": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-1.1.10.tgz", - "integrity": "sha512-PJZmhQ/R1PgdMyCRjALfoyq1FNh/WzMAw70sliHLtLcb9hBXniwQZuckYfUshCkUoFBj/ow9d4byn9Culdpk7Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-2.1.0.tgz", + "integrity": "sha512-bskumBYKLiLMySIWDGcz0+D9Th0jPvmX6xnRMs4o92tT3Od/bW26lahmV2a2Op2ItXeCmRMY+XxJH5Gy1i46VA==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/input": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/input/-/input-1.4.6.tgz", - "integrity": "sha512-Ljy/NbOhh9cNQxKTWQRsT4aQiXs2vVya+Cj5NpMAz08NFFjPZovsTawhI7m6ejT5Vsh76QYjh2rOLLI3fWqQQw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/input/-/input-2.1.2.tgz", + "integrity": "sha512-GiBbb3EqAA8Ph43yGa6Mc+kUPjh4Spmxp1Pkelr8qtudpc3p2PJOOebLpd90mcqw8UePPa+l6YhhPtp6o0irhw==", "dependencies": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/layout": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/layout/-/layout-1.8.0.tgz", - "integrity": "sha512-GJtEKez5AZu0XQTxI6a6jwA/hMDD36pP0HBxBOGuHP1hWCebDzMjraiMfWiP9w7hKERFE4j19kocHxIXyocfJA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/layout/-/layout-2.3.1.tgz", + "integrity": "sha512-nXuZ6WRbq0WdgnRgLw+QuxWAHuhDtVX8ElWqcTK+cSMFg/52eVP47czYBE5F35YhnoW2XBwfNoNgZ7+e8Z01Rg==", "dependencies": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/breakpoint-utils": "2.0.8", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, + "node_modules/@chakra-ui/lazy-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@chakra-ui/lazy-utils/-/lazy-utils-2.0.5.tgz", + "integrity": "sha512-UULqw7FBvcckQk2n3iPO56TMJvDsNv0FKZI6PlUNJVaGsPbsYxK/8IQ60vZgaTVPtVcjY6BE+y6zg8u9HOqpyg==" + }, "node_modules/@chakra-ui/live-region": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/live-region/-/live-region-1.1.6.tgz", - "integrity": "sha512-9gPQHXf7oW0jXyT5R/JzyDMfJ3hF70TqhN8bRH4fMyfNr2Se+SjztMBqCrv5FS5rPjcCeua+e0eArpoB3ROuWQ==", - "dependencies": { - "@chakra-ui/utils": "1.10.4" - }, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/live-region/-/live-region-2.1.0.tgz", + "integrity": "sha512-ZOxFXwtaLIsXjqnszYYrVuswBhnIHHP+XIgK1vC6DePKtyK590Wg+0J0slDwThUAd4MSSIUa/nNX84x1GMphWw==", "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, "node_modules/@chakra-ui/media-query": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@chakra-ui/media-query/-/media-query-2.0.4.tgz", - "integrity": "sha512-kn6g/L0IFFUHz2v4yiCsBnhg9jUeA7525Z+AWl+BPtvryi7i9J+AJ27y/QAge7vUGy4dwDeFyxOZTs2oZ9/BsA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/media-query/-/media-query-3.3.0.tgz", + "integrity": "sha512-IsTGgFLoICVoPRp9ykOgqmdMotJG0CnPsKvGQeSFOB/dZfIujdVb14TYxDU4+MURXry1MhJ7LzZhv+Ml7cr8/g==", "dependencies": { - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/breakpoint-utils": "2.0.8", + "@chakra-ui/react-env": "3.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "@chakra-ui/theme": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/menu": { - "version": "1.8.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/menu/-/menu-1.8.11.tgz", - "integrity": "sha512-8K65xItPsdMvSfuGWYIGigOF/QMcy7+D48UIEO/Hu0u0ckd11/JXbpSIFPddH5fYedclJ18PGRohTne487OVjQ==", - "dependencies": { - "@chakra-ui/clickable": "1.2.6", - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/menu/-/menu-2.2.1.tgz", + "integrity": "sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g==", + "dependencies": { + "@chakra-ui/clickable": "2.1.0", + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/lazy-utils": "2.0.5", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-animation-state": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-disclosure": "2.1.0", + "@chakra-ui/react-use-focus-effect": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-outside-click": "2.2.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/transition": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "framer-motion": ">=4.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/modal": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/modal/-/modal-1.11.1.tgz", - "integrity": "sha512-B2BBDonHb04vbPLAWgko1JYBwgW8ZNSLyhTJK+rbrCsRSgazuLTcwq4hdyJqrYNWtaQEfSwpAXqJ7joMZdv59A==", - "dependencies": { - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/focus-lock": "1.2.6", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.4.1" - }, - "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6", - "react-dom": ">=16.8.6" - } - }, - "node_modules/@chakra-ui/modal/node_modules/react-remove-scroll": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.4.1.tgz", - "integrity": "sha512-K7XZySEzOHMTq7dDwcHsZA6Y7/1uX5RsWhRXVYv8rdh+y9Qz2nMwl9RX/Mwnj/j7JstCGmxyfyC0zbVGXYh3mA==", - "dependencies": { - "react-remove-scroll-bar": "^2.1.0", - "react-style-singleton": "^2.1.0", - "tslib": "^1.0.0", - "use-callback-ref": "^1.2.3", - "use-sidecar": "^1.0.1" - }, - "engines": { - "node": ">=8.5.0" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/modal/-/modal-2.3.1.tgz", + "integrity": "sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==", + "dependencies": { + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/focus-lock": "2.1.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/transition": "2.1.0", + "aria-hidden": "^1.2.3", + "react-remove-scroll": "^2.5.6" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0", - "react": "^16.8.0 || ^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@chakra-ui/system": ">=2.0.0", + "framer-motion": ">=4.0.0", + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/@chakra-ui/number-input": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-1.4.7.tgz", - "integrity": "sha512-LorGRZFMipom8vCUEbLi2s7bTHF2Fgiu766W0jTbzMje+8Z1ZoRQunH9OZWQnxnWQTUfUM2KBW8KwToYh1ojfQ==", - "dependencies": { - "@chakra-ui/counter": "1.2.10", - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-2.1.2.tgz", + "integrity": "sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA==", + "dependencies": { + "@chakra-ui/counter": "2.1.0", + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-interval": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, + "node_modules/@chakra-ui/number-utils": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-utils/-/number-utils-2.0.7.tgz", + "integrity": "sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg==" + }, + "node_modules/@chakra-ui/object-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz", + "integrity": "sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ==" + }, "node_modules/@chakra-ui/pin-input": { - "version": "1.7.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/pin-input/-/pin-input-1.7.10.tgz", - "integrity": "sha512-Uz5vFK+ZevQtdYHBkddSFCrY44bweXLanpSv9X/D0pWpdML09qfPiKX4ydGzfRoS2u4L8NUtN86IcvdOQLhHQg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/pin-input/-/pin-input-2.1.0.tgz", + "integrity": "sha512-x4vBqLStDxJFMt+jdAHHS8jbh294O53CPQJoL4g228P513rHylV/uPscYUHrVJXRxsHfRztQO9k45jjTYaPRMw==", "dependencies": { - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/popover": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/@chakra-ui/popover/-/popover-1.11.9.tgz", - "integrity": "sha512-hJ1/Lwukox3ryTN7W1wnj+nE44utfLwQYvfUSdatt5dznnh8k0P6Wx7Hmjm1cYffRavBhqzwua/QZDWjJN9N0g==", - "dependencies": { - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/popover/-/popover-2.2.1.tgz", + "integrity": "sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==", + "dependencies": { + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/lazy-utils": "2.0.5", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-animation-state": "2.1.0", + "@chakra-ui/react-use-disclosure": "2.1.0", + "@chakra-ui/react-use-focus-effect": "2.1.0", + "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "framer-motion": ">=4.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/popper": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/popper/-/popper-2.4.3.tgz", - "integrity": "sha512-TGzFnYt3mtIVkIejtYIAu4Ka9DaYLzMR4NgcqI6EtaTvgK7Xep+6RTiY/Nq+ZT3l/eaNUwqHRFoNrDUg1XYasA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/popper/-/popper-3.1.0.tgz", + "integrity": "sha512-ciDdpdYbeFG7og6/6J8lkTFxsSvwTdMLFkpVylAF6VNC22jssiWfquj2eyD4rJnzkRFPvIWJq8hvbfhsm+AjSg==", "dependencies": { - "@chakra-ui/react-utils": "1.2.3", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", "@popperjs/core": "^2.9.3" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, "node_modules/@chakra-ui/portal": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/portal/-/portal-1.3.10.tgz", - "integrity": "sha512-t2KQ6MXbyf1qFYxWw/bs//CnwD+Clq7mbsP1Y7g+THCz2FvlLlMj45BWocLB30NoNyA8WCS2zyMBszW2/qvDiA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/portal/-/portal-2.1.0.tgz", + "integrity": "sha512-9q9KWf6SArEcIq1gGofNcFPSWEyl+MfJjEUg/un1SMlQjaROOh3zYr+6JAwvcORiX7tyHosnmWC3d3wI2aPSQg==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" }, "peerDependencies": { - "react": ">=16.8.6", - "react-dom": ">=16.8.6" + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/@chakra-ui/progress": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/progress/-/progress-1.2.6.tgz", - "integrity": "sha512-thaHRIYTVktgV78vJMNwzfCX+ickhSpn2bun6FtGVUphFx4tjV+ggz+IGohm6AH2hapskoR1mQU2iNZb6BK0hQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/progress/-/progress-2.2.0.tgz", + "integrity": "sha512-qUXuKbuhN60EzDD9mHR7B67D7p/ZqNS2Aze4Pbl1qGGZfulPW0PY8Rof32qDtttDQBkzQIzFGE8d9QpAemToIQ==", "dependencies": { - "@chakra-ui/theme-tools": "1.3.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/provider": { - "version": "1.7.14", - "resolved": "https://registry.npmjs.org/@chakra-ui/provider/-/provider-1.7.14.tgz", - "integrity": "sha512-FCA33CZy/jFzExglKMioeri8sr9NtDTcNVPnx95ZJiA7WpfFo0xuZ6/fMC4DwIQPkJKbSIZBXYLZ3U10Ntylrw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/provider/-/provider-2.4.2.tgz", + "integrity": "sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==", "dependencies": { - "@chakra-ui/css-reset": "1.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/system": "1.12.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/css-reset": "2.3.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-env": "3.1.0", + "@chakra-ui/system": "2.6.2", + "@chakra-ui/utils": "2.0.15" }, "peerDependencies": { "@emotion/react": "^11.0.0", "@emotion/styled": "^11.0.0", - "react": ">=16.8.6", - "react-dom": ">=16.8.6" + "react": ">=18", + "react-dom": ">=18" } }, + "node_modules/@chakra-ui/provider/node_modules/@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "dependencies": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/provider/node_modules/@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@chakra-ui/provider/node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/@chakra-ui/provider/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/@chakra-ui/radio": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/radio/-/radio-1.5.1.tgz", - "integrity": "sha512-zO5eShz+j68A7935jJ2q5u3brX/bjPEGh9Pj2+bnKbmC9Vva6jEzBSJsAx9n4WbkAzR3xDMGWsbpivFp8X1tJw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/radio/-/radio-2.1.2.tgz", + "integrity": "sha512-n10M46wJrMGbonaghvSRnZ9ToTv/q76Szz284gv4QUWvyljQACcGrXIONUnQ3BIwbOfkRqSk7Xl/JgZtVfll+w==", "dependencies": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@zag-js/focus-visible": "0.16.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@chakra-ui/react": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-1.8.8.tgz", - "integrity": "sha512-/XqL25J0i0h+usAXBngn/RTG2u1oQRzbhHe9tNHwFyNbx/izIADhQW/6ji06QU0KtaRIU77XvgSAyTtMJY1KmA==", - "dependencies": { - "@chakra-ui/accordion": "1.4.11", - "@chakra-ui/alert": "1.3.7", - "@chakra-ui/avatar": "1.3.11", - "@chakra-ui/breadcrumb": "1.3.6", - "@chakra-ui/button": "1.5.10", - "@chakra-ui/checkbox": "1.7.1", - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/control-box": "1.1.6", - "@chakra-ui/counter": "1.2.10", - "@chakra-ui/css-reset": "1.1.3", - "@chakra-ui/editable": "1.4.2", - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/image": "1.1.10", - "@chakra-ui/input": "1.4.6", - "@chakra-ui/layout": "1.8.0", - "@chakra-ui/live-region": "1.1.6", - "@chakra-ui/media-query": "2.0.4", - "@chakra-ui/menu": "1.8.11", - "@chakra-ui/modal": "1.11.1", - "@chakra-ui/number-input": "1.4.7", - "@chakra-ui/pin-input": "1.7.10", - "@chakra-ui/popover": "1.11.9", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/progress": "1.2.6", - "@chakra-ui/provider": "1.7.14", - "@chakra-ui/radio": "1.5.1", - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/select": "1.2.11", - "@chakra-ui/skeleton": "1.2.14", - "@chakra-ui/slider": "1.5.11", - "@chakra-ui/spinner": "1.2.6", - "@chakra-ui/stat": "1.2.7", - "@chakra-ui/switch": "1.3.10", - "@chakra-ui/system": "1.12.1", - "@chakra-ui/table": "1.3.6", - "@chakra-ui/tabs": "1.6.10", - "@chakra-ui/tag": "1.2.7", - "@chakra-ui/textarea": "1.2.11", - "@chakra-ui/theme": "1.14.1", - "@chakra-ui/toast": "1.5.9", - "@chakra-ui/tooltip": "1.5.1", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.8.2.tgz", + "integrity": "sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==", + "dependencies": { + "@chakra-ui/accordion": "2.3.1", + "@chakra-ui/alert": "2.2.2", + "@chakra-ui/avatar": "2.3.0", + "@chakra-ui/breadcrumb": "2.2.0", + "@chakra-ui/button": "2.1.0", + "@chakra-ui/card": "2.2.0", + "@chakra-ui/checkbox": "2.3.2", + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/control-box": "2.1.0", + "@chakra-ui/counter": "2.1.0", + "@chakra-ui/css-reset": "2.3.0", + "@chakra-ui/editable": "3.1.0", + "@chakra-ui/focus-lock": "2.1.0", + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/hooks": "2.2.1", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/image": "2.1.0", + "@chakra-ui/input": "2.1.2", + "@chakra-ui/layout": "2.3.1", + "@chakra-ui/live-region": "2.1.0", + "@chakra-ui/media-query": "3.3.0", + "@chakra-ui/menu": "2.2.1", + "@chakra-ui/modal": "2.3.1", + "@chakra-ui/number-input": "2.1.2", + "@chakra-ui/pin-input": "2.1.0", + "@chakra-ui/popover": "2.2.1", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/progress": "2.2.0", + "@chakra-ui/provider": "2.4.2", + "@chakra-ui/radio": "2.1.2", + "@chakra-ui/react-env": "3.1.0", + "@chakra-ui/select": "2.1.2", + "@chakra-ui/skeleton": "2.1.0", + "@chakra-ui/skip-nav": "2.1.0", + "@chakra-ui/slider": "2.1.0", + "@chakra-ui/spinner": "2.1.0", + "@chakra-ui/stat": "2.1.1", + "@chakra-ui/stepper": "2.3.1", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/switch": "2.1.2", + "@chakra-ui/system": "2.6.2", + "@chakra-ui/table": "2.1.0", + "@chakra-ui/tabs": "3.0.0", + "@chakra-ui/tag": "3.1.1", + "@chakra-ui/textarea": "2.1.2", + "@chakra-ui/theme": "3.3.1", + "@chakra-ui/theme-utils": "2.0.21", + "@chakra-ui/toast": "7.0.2", + "@chakra-ui/tooltip": "2.3.1", + "@chakra-ui/transition": "2.1.0", + "@chakra-ui/utils": "2.0.15", + "@chakra-ui/visually-hidden": "2.2.0" }, "peerDependencies": { "@emotion/react": "^11.0.0", "@emotion/styled": "^11.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6", - "react-dom": ">=16.8.6" + "framer-motion": ">=4.0.0", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@chakra-ui/react-children-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-children-utils/-/react-children-utils-2.0.6.tgz", + "integrity": "sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-context": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.1.0.tgz", + "integrity": "sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==", + "peerDependencies": { + "react": ">=18" } }, "node_modules/@chakra-ui/react-env": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/react-env/-/react-env-1.1.6.tgz", - "integrity": "sha512-L90LNvCfe04FTkN9OPok/o2e60zLJNBH8Im/5dUHvqy7dXLXok8ZDad5vEL46XmGbhe7O8fbxhG6FmAYdcCHrQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-env/-/react-env-3.1.0.tgz", + "integrity": "sha512-Vr96GV2LNBth3+IKzr/rq1IcnkXv+MLmwjQH6C8BRtn3sNskgDFD5vLkVXcEhagzZMCh8FR3V/bzZPojBOyNhw==", "dependencies": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/react-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-1.2.3.tgz", - "integrity": "sha512-r8pUwCVVB7UPhb0AiRa9ZzSp4xkMz64yIeJ4O4aGy4WMw7TRH4j4QkbkE1YC9tQitrXrliOlvx4WWJR4VyiGpw==", + "node_modules/@chakra-ui/react-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-types/-/react-types-2.0.7.tgz", + "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-animation-state": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.1.0.tgz", + "integrity": "sha512-CFZkQU3gmDBwhqy0vC1ryf90BVHxVN8cTLpSyCpdmExUEtSEInSCGMydj2fvn7QXsz/za8JNdO2xxgJwxpLMtg==", "dependencies": { - "@chakra-ui/utils": "^1.10.4" + "@chakra-ui/dom-utils": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0" }, "peerDependencies": { - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/select": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/select/-/select-1.2.11.tgz", - "integrity": "sha512-6Tis1+ZrRjQeWhQfziQn3ZdPphV5ccafpZOhiPdTcM2J1XcXOlII+9rHxvaW+jx7zQ5ly5o8kd7iXzalDgl5wA==", + "node_modules/@chakra-ui/react-use-callback-ref": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.1.0.tgz", + "integrity": "sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ==", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-controllable-state": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.1.0.tgz", + "integrity": "sha512-QR/8fKNokxZUs4PfxjXuwl0fj/d71WPrmLJvEpCTkHjnzu7LnYvzoe2wB867IdooQJL0G1zBxl0Dq+6W1P3jpg==", "dependencies": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-callback-ref": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/skeleton": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@chakra-ui/skeleton/-/skeleton-1.2.14.tgz", - "integrity": "sha512-R0v4DfQ2yjXCJf9SzhTmDb2PLx5//LxsRbjjgRa8qJCR4MZaGswPrekp4dP8YjY8aEYzuZbvHU12T3vqZBk2GA==", + "node_modules/@chakra-ui/react-use-disclosure": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.1.0.tgz", + "integrity": "sha512-Ax4pmxA9LBGMyEZJhhUZobg9C0t3qFE4jVF1tGBsrLDcdBeLR9fwOogIPY9Hf0/wqSlAryAimICbr5hkpa5GSw==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/media-query": "2.0.4", - "@chakra-ui/system": "1.12.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-callback-ref": "2.1.0" }, "peerDependencies": { - "@chakra-ui/theme": ">=1.0.0", - "@emotion/react": "^11.0.0", - "@emotion/styled": "^11.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/slider": { - "version": "1.5.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-1.5.11.tgz", - "integrity": "sha512-THkGU2BsA6XMosXcEVQkWVRftqUIAKCb+y4iEpR3C2ztqL7Fl/CbIGwyr5majhPhKc275rb8dfxwp8R0L0ZIiQ==", + "node_modules/@chakra-ui/react-use-event-listener": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.1.0.tgz", + "integrity": "sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-callback-ref": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/spinner": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/spinner/-/spinner-1.2.6.tgz", - "integrity": "sha512-GoUCccN120fGRVgUtfuwcEjeoaxffB+XsgpxX7jhWloXf8b6lkqm68bsxX4Ybb2vGN1fANI98/45JmrnddZO/A==", + "node_modules/@chakra-ui/react-use-focus-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.1.0.tgz", + "integrity": "sha512-xzVboNy7J64xveLcxTIJ3jv+lUJKDwRM7Szwn9tNzUIPD94O3qwjV7DDCUzN2490nSYDF4OBMt/wuDBtaR3kUQ==", "dependencies": { - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/dom-utils": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/stat": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/stat/-/stat-1.2.7.tgz", - "integrity": "sha512-m76jumFW1N+mCG4ytrUz9Mh09nZtS4OQcADEvOslfdI5StwwuzasTA1tueaelPzdhBioMwFUWL05Fr1fXbPJ/Q==", + "node_modules/@chakra-ui/react-use-focus-on-pointer-down": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.1.0.tgz", + "integrity": "sha512-2jzrUZ+aiCG/cfanrolsnSMDykCAbv9EK/4iUyZno6BYb3vziucmvgKuoXbMPAzWNtwUwtuMhkby8rc61Ue+Lg==", "dependencies": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/react-use-event-listener": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/styled-system": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-1.19.0.tgz", - "integrity": "sha512-z+bMfWs6jQGkpgarge1kmk78DuDhJIXRUMyRqZ3+CiIkze88bIIsww6mV2i8tEfUfTAvALeMnlYZ1DYsHsTTJw==", + "node_modules/@chakra-ui/react-use-interval": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-interval/-/react-use-interval-2.1.0.tgz", + "integrity": "sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw==", "dependencies": { - "@chakra-ui/utils": "1.10.4", - "csstype": "3.0.9" + "@chakra-ui/react-use-callback-ref": "2.1.0" + }, + "peerDependencies": { + "react": ">=18" } }, - "node_modules/@chakra-ui/styled-system/node_modules/csstype": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", - "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" + "node_modules/@chakra-ui/react-use-latest-ref": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.1.0.tgz", + "integrity": "sha512-m0kxuIYqoYB0va9Z2aW4xP/5b7BzlDeWwyXCH6QpT2PpW3/281L3hLCm1G0eOUcdVlayqrQqOeD6Mglq+5/xoQ==", + "peerDependencies": { + "react": ">=18" + } }, - "node_modules/@chakra-ui/switch": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/switch/-/switch-1.3.10.tgz", - "integrity": "sha512-V6qDLY6oECCbPyu7alWWOAhSBI4+SAuT6XW/zEQbelkwuUOiGO1ax67rTXOmZ59A2AaV1gqQFxDh8AcbvwO5XQ==", + "node_modules/@chakra-ui/react-use-merge-refs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz", + "integrity": "sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-outside-click": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.2.0.tgz", + "integrity": "sha512-PNX+s/JEaMneijbgAM4iFL+f3m1ga9+6QK0E5Yh4s8KZJQ/bLwZzdhMz8J/+mL+XEXQ5J0N8ivZN28B82N1kNw==", "dependencies": { - "@chakra-ui/checkbox": "1.7.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-callback-ref": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/system": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-1.12.1.tgz", - "integrity": "sha512-Rp09/rMuPA3hF38OJxeQciGO9N0Ie1GxwHRAw1AFA/TY3fVyK9pNI5oN+J/1cAxq7v9yKdIr1YfnruJTI9xfEg==", + "node_modules/@chakra-ui/react-use-pan-event": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.1.0.tgz", + "integrity": "sha512-xmL2qOHiXqfcj0q7ZK5s9UjTh4Gz0/gL9jcWPA6GVf+A0Od5imEDa/Vz+533yQKWiNSm1QGrIj0eJAokc7O4fg==", "dependencies": { - "@chakra-ui/color-mode": "1.4.8", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/styled-system": "1.19.0", - "@chakra-ui/utils": "1.10.4", - "react-fast-compare": "3.2.0" + "@chakra-ui/event-utils": "2.0.8", + "@chakra-ui/react-use-latest-ref": "2.1.0", + "framesync": "6.1.2" }, "peerDependencies": { - "@emotion/react": "^11.0.0", - "@emotion/styled": "^11.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/system/node_modules/react-fast-compare": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", - "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + "node_modules/@chakra-ui/react-use-pan-event/node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "dependencies": { + "tslib": "2.4.0" + } }, - "node_modules/@chakra-ui/table": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/table/-/table-1.3.6.tgz", - "integrity": "sha512-7agZAgAeDFKviqStvixqnLAH54+setzhx67EztioZTr5Xu+6hQ4rotfJbu8L4i587pcbNg98kCEXEkidjw0XRQ==", + "node_modules/@chakra-ui/react-use-pan-event/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@chakra-ui/react-use-previous": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-previous/-/react-use-previous-2.1.0.tgz", + "integrity": "sha512-pjxGwue1hX8AFcmjZ2XfrQtIJgqbTF3Qs1Dy3d1krC77dEsiCUbQ9GzOBfDc8pfd60DrB5N2tg5JyHbypqh0Sg==", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-safe-layout-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz", + "integrity": "sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-size": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-size/-/react-use-size-2.1.0.tgz", + "integrity": "sha512-tbLqrQhbnqOjzTaMlYytp7wY8BW1JpL78iG7Ru1DlV4EWGiAmXFGvtnEt9HftU0NJ0aJyjgymkxfVGI55/1Z4A==", "dependencies": { - "@chakra-ui/utils": "1.10.4" + "@zag-js/element-size": "0.10.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/tabs": { - "version": "1.6.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/tabs/-/tabs-1.6.10.tgz", - "integrity": "sha512-ClOOHT3Wnf3l9X4F2S6ysPsHMDgKSTgkXpB9Qe0odwpT49ZXNjSAYYaXzO16l+Eq/m2u1HzLkXVsL42HIeOiNQ==", + "node_modules/@chakra-ui/react-use-timeout": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-timeout/-/react-use-timeout-2.1.0.tgz", + "integrity": "sha512-cFN0sobKMM9hXUhyCofx3/Mjlzah6ADaEl/AXl5Y+GawB5rgedgAcu2ErAgarEkwvsKdP6c68CKjQ9dmTQlJxQ==", "dependencies": { - "@chakra-ui/clickable": "1.2.6", - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-callback-ref": "2.1.0" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/tag": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/tag/-/tag-1.2.7.tgz", - "integrity": "sha512-RKrKOol4i/CnpFfo3T9LMm1abaqM+5Bs0soQLbo1iJBbBACY09sWXrQYvveQ2GYzU/OrAUloHqqmKjyVGOlNtg==", + "node_modules/@chakra-ui/react-use-update-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.1.0.tgz", + "integrity": "sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA==", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-utils": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz", + "integrity": "sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==", "dependencies": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/utils": "2.0.15" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "react": ">=18" } }, - "node_modules/@chakra-ui/textarea": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/textarea/-/textarea-1.2.11.tgz", - "integrity": "sha512-RDWbMyC87/AFRX98EnVum5eig/7hhcvS1BrqW5lvmTgrpr7KVr80Dfa8hUj58Iq37Z7AqZijDPkBn/zg7bPdIg==", + "node_modules/@chakra-ui/react-utils/node_modules/@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "dependencies": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/react-utils/node_modules/@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@chakra-ui/react-utils/node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/@chakra-ui/react-utils/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@chakra-ui/react/node_modules/@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "dependencies": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/react/node_modules/@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@chakra-ui/react/node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/@chakra-ui/react/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@chakra-ui/select": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/select/-/select-2.1.2.tgz", + "integrity": "sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==", "dependencies": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/theme": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-1.14.1.tgz", - "integrity": "sha512-VeNZi+zD3yDwzvZm234Cy3vnalCzQ+dhAgpHdIYzGO1CYO8DPa+ROcQ70rUueL7dSvUz15KOiGTw6DAl7LXlGA==", + "node_modules/@chakra-ui/shared-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz", + "integrity": "sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q==" + }, + "node_modules/@chakra-ui/skeleton": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/skeleton/-/skeleton-2.1.0.tgz", + "integrity": "sha512-JNRuMPpdZGd6zFVKjVQ0iusu3tXAdI29n4ZENYwAJEMf/fN0l12sVeirOxkJ7oEL0yOx2AgEYFSKdbcAgfUsAQ==", "dependencies": { - "@chakra-ui/anatomy": "1.3.0", - "@chakra-ui/theme-tools": "1.3.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/media-query": "3.3.0", + "@chakra-ui/react-use-previous": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/theme-tools": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-1.3.6.tgz", - "integrity": "sha512-Wxz3XSJhPCU6OwCHEyH44EegEDQHwvlsx+KDkUDGevOjUU88YuNqOVkKtgTpgMLNQcsrYZ93oPWZUJqqCVNRew==", + "node_modules/@chakra-ui/skip-nav": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/skip-nav/-/skip-nav-2.1.0.tgz", + "integrity": "sha512-Hk+FG+vadBSH0/7hwp9LJnLjkO0RPGnx7gBJWI4/SpoJf3e4tZlWYtwGj0toYY4aGKl93jVghuwGbDBEMoHDug==", + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/slider": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-2.1.0.tgz", + "integrity": "sha512-lUOBcLMCnFZiA/s2NONXhELJh6sY5WtbRykPtclGfynqqOo47lwWJx+VP7xaeuhDOPcWSSecWc9Y1BfPOCz9cQ==", + "dependencies": { + "@chakra-ui/number-utils": "2.0.7", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-latest-ref": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-pan-event": "2.1.0", + "@chakra-ui/react-use-size": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/spinner": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/spinner/-/spinner-2.1.0.tgz", + "integrity": "sha512-hczbnoXt+MMv/d3gE+hjQhmkzLiKuoTo42YhUG7Bs9OSv2lg1fZHW1fGNRFP3wTi6OIbD044U1P9HK+AOgFH3g==", "dependencies": { - "@chakra-ui/utils": "1.10.4", - "@ctrl/tinycolor": "^3.4.0" + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/toast": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@chakra-ui/toast/-/toast-1.5.9.tgz", - "integrity": "sha512-rns04bGdMcG7Ijg45L+PfuEW4rCd0Ycraix4EJQhcl9RXI18G9sphmlp9feidhZAkI6Ukafq1YvyvkBfkKnIzQ==", + "node_modules/@chakra-ui/stat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/stat/-/stat-2.1.1.tgz", + "integrity": "sha512-LDn0d/LXQNbAn2KaR3F1zivsZCewY4Jsy1qShmfBMKwn6rI8yVlbvu6SiA3OpHS0FhxbsZxQI6HefEoIgtqY6Q==", "dependencies": { - "@chakra-ui/alert": "1.3.7", - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/theme": "1.14.1", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4", - "@reach/alert": "0.13.2" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6", - "react-dom": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/toast/node_modules/@reach/alert": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@reach/alert/-/alert-0.13.2.tgz", - "integrity": "sha512-LDz83AXCrClyq/MWe+0vaZfHp1Ytqn+kgL5VxG7rirUvmluWaj/snxzfNPWn0Ma4K2YENmXXRC/iHt5X95SqIg==", + "node_modules/@chakra-ui/stepper": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/stepper/-/stepper-2.3.1.tgz", + "integrity": "sha512-ky77lZbW60zYkSXhYz7kbItUpAQfEdycT0Q4bkHLxfqbuiGMf8OmgZOQkOB9uM4v0zPwy2HXhe0vq4Dd0xa55Q==", "dependencies": { - "@reach/utils": "0.13.2", - "@reach/visually-hidden": "0.13.2", - "prop-types": "^15.7.2", - "tslib": "^2.1.0" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/toast/node_modules/@reach/alert/node_modules/@reach/utils": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.13.2.tgz", - "integrity": "sha512-3ir6cN60zvUrwjOJu7C6jec/samqAeyAB12ZADK+qjnmQPdzSYldrFWwDVV5H0WkhbYXR3uh+eImu13hCetNPQ==", + "node_modules/@chakra-ui/styled-system": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz", + "integrity": "sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg==", "dependencies": { - "@types/warning": "^3.0.0", - "tslib": "^2.1.0", - "warning": "^4.0.3" + "@chakra-ui/shared-utils": "2.0.5", + "csstype": "^3.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/switch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/switch/-/switch-2.1.2.tgz", + "integrity": "sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==", + "dependencies": { + "@chakra-ui/checkbox": "2.3.2", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x" + "@chakra-ui/system": ">=2.0.0", + "framer-motion": ">=4.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/toast/node_modules/@reach/alert/node_modules/@reach/visually-hidden": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@reach/visually-hidden/-/visually-hidden-0.13.2.tgz", - "integrity": "sha512-sPZwNS0/duOuG0mYwE5DmgEAzW9VhgU3aIt1+mrfT/xiT9Cdncqke+kRBQgU708q/Ttm9tWsoHni03nn/SuPTQ==", + "node_modules/@chakra-ui/system": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-2.6.2.tgz", + "integrity": "sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==", "dependencies": { - "prop-types": "^15.7.2", - "tslib": "^2.1.0" + "@chakra-ui/color-mode": "2.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-utils": "2.0.12", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme-utils": "2.0.21", + "@chakra-ui/utils": "2.0.15", + "react-fast-compare": "3.2.2" }, "peerDependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x" + "@emotion/react": "^11.0.0", + "@emotion/styled": "^11.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/system/node_modules/@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "dependencies": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" } }, - "node_modules/@chakra-ui/toast/node_modules/tslib": { + "node_modules/@chakra-ui/system/node_modules/@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@chakra-ui/system/node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/@chakra-ui/system/node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, + "node_modules/@chakra-ui/system/node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, - "node_modules/@chakra-ui/tooltip": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/tooltip/-/tooltip-1.5.1.tgz", - "integrity": "sha512-EUAlDdlCBt63VpEVtj/RkFjHQVN/xA9gEAumngQdi1Sp+OXPYCBM9GwSY0NwrM1RfKBnhPSH9wz7FwredJWeaw==", + "node_modules/@chakra-ui/table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/table/-/table-2.1.0.tgz", + "integrity": "sha512-o5OrjoHCh5uCLdiUb0Oc0vq9rIAeHSIRScc2ExTC9Qg/uVZl2ygLrjToCaKfaaKl1oQexIeAcZDKvPG8tVkHyQ==", "dependencies": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6", - "react-dom": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/transition": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@chakra-ui/transition/-/transition-1.4.8.tgz", - "integrity": "sha512-5uc8LEuCH7+0h++wqAav/EktTHOjbLDSTXQlU9fzPIlNNgyf2eXrHVN2AGMGKiMR9Z4gS7umQjZ54r0w/mZ/Fw==", + "node_modules/@chakra-ui/tabs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/tabs/-/tabs-3.0.0.tgz", + "integrity": "sha512-6Mlclp8L9lqXmsGWF5q5gmemZXOiOYuh0SGT/7PgJVNPz3LXREXlXg2an4MBUD8W5oTkduCX+3KTMCwRrVrDYw==", + "dependencies": { + "@chakra-ui/clickable": "2.1.0", + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/lazy-utils": "2.0.5", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/tag": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/tag/-/tag-3.1.1.tgz", + "integrity": "sha512-Bdel79Dv86Hnge2PKOU+t8H28nm/7Y3cKd4Kfk9k3lOpUh4+nkSGe58dhRzht59lEqa4N9waCgQiBdkydjvBXQ==", "dependencies": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0" }, "peerDependencies": { - "framer-motion": "3.x || 4.x || 5.x || 6.x", - "react": ">=16.8.6" + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, - "node_modules/@chakra-ui/utils": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-1.10.4.tgz", - "integrity": "sha512-AM91VQQxw8F4F1WDA28mqKY6NFIOuzc2Ekkna88imy2OiqqmYH0xkq8J16L2qj4cLiLozpYqba3C79pWioy6FA==", + "node_modules/@chakra-ui/textarea": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/textarea/-/textarea-2.1.2.tgz", + "integrity": "sha512-ip7tvklVCZUb2fOHDb23qPy/Fr2mzDOGdkrpbNi50hDCiV4hFX02jdQJdi3ydHZUyVgZVBKPOJ+lT9i7sKA2wA==", "dependencies": { - "@types/lodash.mergewith": "4.6.6", - "css-box-model": "1.2.1", - "framesync": "5.3.0", + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/theme": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.3.1.tgz", + "integrity": "sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ==", + "dependencies": { + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/theme-tools": "2.1.2" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.8.0" + } + }, + "node_modules/@chakra-ui/theme-tools": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz", + "integrity": "sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA==", + "dependencies": { + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "color2k": "^2.0.2" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.0.0" + } + }, + "node_modules/@chakra-ui/theme-utils": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz", + "integrity": "sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw==", + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme": "3.3.1", "lodash.mergewith": "4.6.2" } }, - "node_modules/@chakra-ui/visually-hidden": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/visually-hidden/-/visually-hidden-1.1.6.tgz", - "integrity": "sha512-Xzy5bA0UA+IyMgwJizQYSEdgz8cC/tHdmFB3CniXzmpKTSK8mJddeEBl+cGbXHBzxEUhH7xF1eaS41O+0ezWEQ==", + "node_modules/@chakra-ui/toast": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/toast/-/toast-7.0.2.tgz", + "integrity": "sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==", + "dependencies": { + "@chakra-ui/alert": "2.2.2", + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-timeout": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme": "3.3.1" + }, + "peerDependencies": { + "@chakra-ui/system": "2.6.2", + "framer-motion": ">=4.0.0", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@chakra-ui/tooltip": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/tooltip/-/tooltip-2.3.1.tgz", + "integrity": "sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==", + "dependencies": { + "@chakra-ui/dom-utils": "2.1.0", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-disclosure": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "framer-motion": ">=4.0.0", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@chakra-ui/transition": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/transition/-/transition-2.1.0.tgz", + "integrity": "sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ==", "dependencies": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/shared-utils": "2.0.5" }, "peerDependencies": { - "@chakra-ui/system": ">=1.0.0", - "react": ">=16.8.6" + "framer-motion": ">=4.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/visually-hidden": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/visually-hidden/-/visually-hidden-2.2.0.tgz", + "integrity": "sha512-KmKDg01SrQ7VbTD3+cPWf/UfpF5MSwm3v7MWi0n5t8HnnadT13MF0MJCDSXbBWnzLv1ZKJ6zlyAOeARWX+DpjQ==", + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" } }, "node_modules/@cnakazawa/watch": { @@ -3144,14 +3571,6 @@ "node": ">=0.1.95" } }, - "node_modules/@ctrl/tinycolor": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz", - "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==", - "engines": { - "node": ">=10" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz", @@ -3212,18 +3631,6 @@ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, - "node_modules/@emotion/css": { - "version": "11.11.2", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.11.2.tgz", - "integrity": "sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew==", - "dependencies": { - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1" - } - }, "node_modules/@emotion/hash": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", @@ -3345,29 +3752,430 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, - "node_modules/@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", - "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "lodash.get": "^4", - "make-error": "^1", - "ts-node": "^9", - "tslib": "^2" - }, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "cosmiconfig": ">=6" + "node": ">=18" } }, - "node_modules/@endemolshinegroup/cosmiconfig-typescript-loader/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true + "node_modules/@esbuild/android-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", @@ -3475,6 +4283,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@gilbarbara/deep-equal": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz", + "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw==" + }, "node_modules/@graphql-tools/merge": { "version": "8.3.3", "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.3.tgz", @@ -3677,6 +4490,102 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -4133,6 +5042,19 @@ "node": ">=8" } }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/source-map": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", @@ -4302,15 +5224,6 @@ "node": ">= 10.14.2" } }, - "node_modules/@jest/types/node_modules/@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, "node_modules/@jest/types/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4398,31 +5311,52 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "devOptional": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "devOptional": true, "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "devOptional": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", - "devOptional": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -4434,174 +5368,222 @@ "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" }, "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, "node_modules/@lingui/babel-plugin-extract-messages": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.14.0.tgz", - "integrity": "sha512-4lcDgVdjYiObuFdDwnAG3jJxS+d3YLq4i7qywlHgjIqteKUH01S3paJRXhZaPvLGl56HarSq0kt8Pymxw8lOrA==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-5.9.3.tgz", + "integrity": "sha512-zm6QHDILmhj8olgLL2zHQn18yFA5mf4hX7QzCr1OOI/e815I0IkecCYue1Ych+y+B+V0eLriiW8AcfpDRCQFFw==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@lingui/babel-plugin-lingui-macro": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-lingui-macro/-/babel-plugin-lingui-macro-5.9.3.tgz", + "integrity": "sha512-fLMhBarRsuqBGOq2YuEoqOjEAV2VNezz/+f/Dn0vLFHF/kAjnFwTHb8pL8DRSIMsWG16mPrGnLpdROZBmJlFtA==", + "devOptional": true, + "license": "MIT", "dependencies": { - "@babel/generator": "^7.11.6", - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.14.0", - "mkdirp": "^1.0.4" + "@babel/core": "^7.20.12", + "@babel/runtime": "^7.20.13", + "@babel/types": "^7.20.7", + "@lingui/conf": "5.9.3", + "@lingui/core": "5.9.3", + "@lingui/message-utils": "5.9.3" }, "engines": { - "node": ">=10.0.0" + "node": ">=20.0.0" + }, + "peerDependencies": { + "babel-plugin-macros": "2 || 3" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, "node_modules/@lingui/cli": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.14.0.tgz", - "integrity": "sha512-QZURsIf7A97tf28b/ffpeL0DekA6tBmcwnj4FBui1SbQqJw1d4IPg2bUM5VRn3/25vhqpi9Uhx5m9x7Vv8QfCQ==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.11.6", - "@babel/parser": "^7.11.5", - "@babel/plugin-syntax-jsx": "^7.10.4", - "@babel/runtime": "^7.11.2", - "@babel/types": "^7.11.5", - "@lingui/babel-plugin-extract-messages": "^3.14.0", - "@lingui/conf": "^3.14.0", - "babel-plugin-macros": "^3.0.1", - "bcp-47": "^1.0.7", - "chalk": "^4.1.0", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-5.9.3.tgz", + "integrity": "sha512-KEE0J4eGlfpiLZ+l019qjraWfqfh5mUmQSJeTFw5PulO4v50zvxw5tDX8stpBzJ3QtgUQZlrMUh0OTGdURaAMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.0", + "@babel/generator": "^7.21.1", + "@babel/parser": "^7.22.0", + "@babel/runtime": "^7.21.0", + "@babel/types": "^7.21.2", + "@lingui/babel-plugin-extract-messages": "5.9.3", + "@lingui/babel-plugin-lingui-macro": "5.9.3", + "@lingui/conf": "5.9.3", + "@lingui/core": "5.9.3", + "@lingui/format-po": "5.9.3", + "@lingui/message-utils": "5.9.3", "chokidar": "3.5.1", - "cli-table": "0.3.6", - "commander": "^6.1.0", - "date-fns": "^2.16.1", - "fs-extra": "^9.0.1", - "fuzzaldrin": "^2.1.0", - "glob": "^7.1.4", - "inquirer": "^7.3.3", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3", - "micromatch": "4.0.2", - "mkdirp": "^1.0.4", - "node-gettext": "^3.0.0", + "cli-table": "^0.3.11", + "commander": "^10.0.0", + "convert-source-map": "^2.0.0", + "date-fns": "^3.6.0", + "esbuild": "^0.25.1", + "glob": "^11.0.0", + "micromatch": "^4.0.7", + "ms": "^2.1.3", "normalize-path": "^3.0.0", "ora": "^5.1.0", - "papaparse": "^5.3.0", - "pkg-up": "^3.1.0", - "plurals-cldr": "^1.0.4", - "pofile": "^1.1.0", - "pseudolocale": "^1.1.0", - "ramda": "^0.27.1" + "picocolors": "^1.1.1", + "pofile": "^1.1.4", + "pseudolocale": "^2.0.0", + "source-map": "^0.7.6", + "threads": "^1.7.0" }, "bin": { - "lingui": "lingui.js" + "lingui": "dist/lingui.js" }, "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "babel-plugin-macros": "2 || 3", - "typescript": "2 || 3 || 4" + "node": ">=20.0.0" } }, - "node_modules/@lingui/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@lingui/cli/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@lingui/cli/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "18 || 20 || >=22" } }, - "node_modules/@lingui/cli/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/@lingui/cli/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@lingui/cli/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=10" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lingui/cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@lingui/cli/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=7.0.0" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lingui/cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/@lingui/cli/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, - "node_modules/@lingui/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@lingui/cli/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">= 12" } }, - "node_modules/@lingui/cli/node_modules/make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==", - "dev": true - }, - "node_modules/@lingui/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/@lingui/conf": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-5.9.3.tgz", + "integrity": "sha512-hVEoYHmO2A3XmFX4A5RuBgcoVBoM7Xgoqejeq25XELvesJj2s2T15F47TA5n3/S7iTqngd6n/8KxBli9TYwgqQ==", + "devOptional": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@babel/runtime": "^7.20.13", + "cosmiconfig": "^8.0.0", + "jest-validate": "^29.4.3", + "jiti": "^2.5.1", + "picocolors": "^1.1.1" }, "engines": { - "node": ">=8" + "node": ">=20.0.0" } }, - "node_modules/@lingui/conf": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.14.0.tgz", - "integrity": "sha512-5GMAbIRad9FavqYsfZCRAwjcOLzE7tONDJe9lSYE5SSJbbG01RI5kR5P0B84DUhTI6cGXau+1dAcP9K+JbEx+g==", - "dev": true, + "node_modules/@lingui/conf/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "devOptional": true, + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.11.2", - "@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "jest-validate": "^26.5.2", - "lodash.get": "^4.4.2" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">=10.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@lingui/conf/node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" } }, "node_modules/@lingui/conf/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4612,11 +5594,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@lingui/conf/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "devOptional": true, + "license": "Python-2.0" + }, "node_modules/@lingui/conf/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4632,7 +5622,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4644,22 +5635,128 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devOptional": true, + "license": "MIT" + }, + "node_modules/@lingui/conf/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, "node_modules/@lingui/conf/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, + "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/@lingui/conf/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@lingui/conf/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@lingui/conf/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@lingui/conf/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@lingui/conf/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@lingui/conf/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@lingui/conf/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4668,74 +5765,135 @@ } }, "node_modules/@lingui/core": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.14.0.tgz", - "integrity": "sha512-ertREq9oi9B/umxpd/pInm9uFO8FLK2/0FXfDmMqvH5ydswWn/c9nY5YO4W1h4/8LWO45mewypOIyjoue4De1w==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.9.3.tgz", + "integrity": "sha512-3b8LnDjx8POdQ6q6UKBe2DHynyQFCO66vm8/UPQnvlQowUk4Xdu5bK6oet11D9/vrSznrDDS+Qb5JVcNBUImgg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.11.2", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3" + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "5.9.3" }, "engines": { - "node": ">=10.0.0" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@lingui/babel-plugin-lingui-macro": "5.9.3", + "babel-plugin-macros": "2 || 3" + }, + "peerDependenciesMeta": { + "@lingui/babel-plugin-lingui-macro": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/@lingui/core/node_modules/make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" + "node_modules/@lingui/format-po": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-5.9.3.tgz", + "integrity": "sha512-+LMnhWl7EmXrdOv10gopE1g8w8vtPY5Fxk72OORrGQFVMGBIioz4BEnKrNdV1ek2M+GxoMZtnUs17KrJN5Jv9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lingui/conf": "5.9.3", + "@lingui/message-utils": "5.9.3", + "date-fns": "^3.6.0", + "pofile": "^1.1.4" + }, + "engines": { + "node": ">=20.0.0" + } }, "node_modules/@lingui/loader": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-3.14.0.tgz", - "integrity": "sha512-FH14MexRLM8tBgSL1oK5dbaUBbY0P85CSLTNwFqAtFFj8K8wWxfdq4+rIkp/kSD+eggKNDYV4G89N9/3bsm3gQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-5.9.3.tgz", + "integrity": "sha512-V+m8vfZ1doPSc26fPZa1zVso75nl70mgdz51OHGAvFfafyDqYasFHmBf5xihS58vy6LXuep3K19Ymf6TG6i5TQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/cli": "^3.14.0", - "@lingui/conf": "^3.14.0", - "loader-utils": "^2.0.0", - "ramda": "^0.27.1" + "@babel/runtime": "^7.20.13", + "@lingui/cli": "5.9.3", + "@lingui/conf": "5.9.3" }, "engines": { - "node": ">=10.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "webpack": "^5.0.0" } }, "node_modules/@lingui/macro": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.14.0.tgz", - "integrity": "sha512-NxTRrhrZ/cUO9PX/4vWys90Ku58+ExxHuE30IuDnnDldWhWlOdycmjDt9tB+yIiUdFym/veSxBs+h114FzG5mA==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-5.9.3.tgz", + "integrity": "sha512-xWqJ+hpp5T+xmE++VYlcfMxrF48LpY5Ak9N/luibY9d0AqvyYZiiv7Xaq7E2eK69v9CJWx+6eXA6uPhC8gHY+Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.14.0", - "ramda": "^0.27.1" + "@lingui/core": "5.9.3", + "@lingui/react": "5.9.3" }, "engines": { - "node": ">=10.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "@lingui/core": "^3.13.0", - "@lingui/react": "^3.13.0", - "babel-plugin-macros": "2 || 3" + "@lingui/babel-plugin-lingui-macro": "5.9.3", + "babel-plugin-macros": "2 || 3" + }, + "peerDependenciesMeta": { + "@lingui/babel-plugin-lingui-macro": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/@lingui/message-utils": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.9.3.tgz", + "integrity": "sha512-oAK7HA7lcQrzaEaM6G1T5RwwxJxaSKfG/IFIxpZIl49TSFQv+s9YPNgHnVi+d4DmterpXNxy9ZZ+NtckJx6u7g==", + "license": "MIT", + "dependencies": { + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@lingui/react": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/react/-/react-3.14.0.tgz", - "integrity": "sha512-ow9Mtru7f0T2S9AwnPWRejppcucCW0LmoDR3P4wqHjL+eH5f8a6nxd2doxGieC91/2i4qqW88y4K/zXJxwRSQw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/react/-/react-5.9.3.tgz", + "integrity": "sha512-aje78l3zGGZ3C75fiGhDVKyVALHfiKlYFjcOlZpgXY/JAVfFuZX+6wUGG9x1A8k7BfxrDy/ofHIBahPvNAqoKw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/core": "^3.14.0" + "@babel/runtime": "^7.20.13", + "@lingui/core": "5.9.3" }, "engines": { - "node": ">=10.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@lingui/babel-plugin-lingui-macro": "5.9.3", + "babel-plugin-macros": "2 || 3", + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@lingui/babel-plugin-lingui-macro": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/@messageformat/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==", + "dependencies": { + "moo": "^0.5.1" } }, "node_modules/@nicolo-ribaudo/chokidar-2": { @@ -4745,6 +5903,18 @@ "dev": true, "optional": true }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4780,10 +5950,248 @@ "node": ">= 8" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.7.0.tgz", + "integrity": "sha512-hew63shtzzvBcSHbhm+cyAmKe6AIfinT9hzEqSPjDC6opTTMKmTkQ0gHuN2KsWlvqiKw1S/fS94fhag/FJkioQ==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "@peculiar/asn1-x509-attr": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-cms/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.7.0.tgz", + "integrity": "sha512-VVsAyGqErT9D1SY4aEqozThXMVI+ssVRiv2DDeYuvpBKLIgZ3hYs3Ay3u/VSoKq6ESFi9cf6rf3IOOzfwh7oMA==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.7.0.tgz", + "integrity": "sha512-n7KEs/Q/wrB415cxy4fHOBhegp4NdJ15fkJPwcB/3/8iNBQC2L/N7SChJPKDJPZGYH0jD4Tg4/0vnHmwghnbKw==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.7.0.tgz", + "integrity": "sha512-V/nrlQVmhg7lYAsM7E13UDL5erAwFv6kCIVFqNaMIHSVi7dngcT839JkRTkQBqznMG98l2XjxYk74ZztAohZzA==", + "dev": true, + "dependencies": { + "@peculiar/asn1-cms": "^2.7.0", + "@peculiar/asn1-pkcs8": "^2.7.0", + "@peculiar/asn1-rsa": "^2.7.0", + "@peculiar/asn1-schema": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.7.0.tgz", + "integrity": "sha512-9GTl1nE8Mx1kTZ+7QyYatDyKsm34QcWRBFkY1iPvWC3X4Dona5s/tlLiQsx5WzVdZqiMBZNYT0buyw4/vbhnjw==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.7.0.tgz", + "integrity": "sha512-Bh7m+OuIaSEllPQcSd9OSp93F4ROWH7sbITWV8MI+8dwsjE5111/87VxiWVvYFKyww3vp39geLv9ENqhwWHcew==", + "dev": true, + "dependencies": { + "@peculiar/asn1-cms": "^2.7.0", + "@peculiar/asn1-pfx": "^2.7.0", + "@peculiar/asn1-pkcs8": "^2.7.0", + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "@peculiar/asn1-x509-attr": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.7.0.tgz", + "integrity": "sha512-/qvENQrXyTZURjMqSeofHul0JJt2sNSzSwk36pl2olkHbaioMQgrASDZAlHXl0xUlnVbHj0uGgOrBMTb5x2aJQ==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.7.0.tgz", + "integrity": "sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg==", + "dev": true, + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.7.0.tgz", + "integrity": "sha512-mUn9RRrkGDnG4ALfunDmzyRW5dg+sWCj/pfnCCqEHYbkGxEpvUt6iVJv8Yw1cyp6SWZ26ZE5oSmI5SqEaen15g==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.7.0.tgz", + "integrity": "sha512-NS8e7SOgXipkzUPLF/sce7ukpMpWjhxYsH0n6Y+bHYo4TTxOb95Zv7hqwSuL212mj5YxovjdOKQOgH1As3E94w==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/asn1-x509/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/utils/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/@peculiar/x509": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", + "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", + "dev": true, + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-csr": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.0", + "@peculiar/asn1-pkcs9": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@peculiar/x509/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, "node_modules/@popperjs/core": { - "version": "2.11.5", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", - "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -4848,11 +6256,26 @@ "dev": true }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "dev": true }, + "node_modules/@remix-run/router": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz", + "integrity": "sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -5217,9 +6640,9 @@ } }, "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -5235,9 +6658,9 @@ } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", @@ -5250,17 +6673,32 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, + "node_modules/@types/d3-array": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz", + "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==" + }, "node_modules/@types/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.5.tgz", + "integrity": "sha512-5sNP3DmtSnSozxcjqmzQKsDOuVJXZkceo1KJScDc1982kk/TS9mTPc6lpli1gTu1MIBF1YWutpHpjucNWcIj5g==" + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", + "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==" }, - "node_modules/@types/d3-interpolate": { + "node_modules/@types/d3-format": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", + "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", "dependencies": { - "@types/d3-color": "*" + "@types/geojson": "*" } }, "node_modules/@types/d3-path": { @@ -5268,13 +6706,10 @@ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz", "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==" }, - "node_modules/@types/d3-scale": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", - "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", - "dependencies": { - "@types/d3-time": "*" - } + "node_modules/@types/d3-random": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-2.2.3.tgz", + "integrity": "sha512-Ghs4R3CcgJ3o6svszRzIH4b8PPYex/COo+rhhZjDAs+bVducXwjmVSi27WcDOaLLCBV2t3tfVH9bYXAL76IvQA==" }, "node_modules/@types/d3-shape": { "version": "1.3.8", @@ -5285,14 +6720,19 @@ } }, "node_modules/@types/d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.4.tgz", + "integrity": "sha512-BTfLsxTeo7yFxI/haOOf1ZwJ6xKgQLT9dCp+EcmQv87Gox6X+oKl4mLKfO6fnWm3P22+A6DknMNEZany8ql2Rw==" + }, + "node_modules/@types/d3-time-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz", + "integrity": "sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==" }, "node_modules/@types/eslint": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "dependencies": { "@types/estree": "*", @@ -5300,9 +6740,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -5310,9 +6750,9 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "node_modules/@types/express": { @@ -5338,6 +6778,11 @@ "@types/range-parser": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.13", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", + "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==" + }, "node_modules/@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -5363,6 +6808,12 @@ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", "dev": true }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true + }, "node_modules/@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -5376,17 +6827,26 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true + "devOptional": true }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "devOptional": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, "node_modules/@types/jest": { "version": "26.0.15", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz", @@ -5398,9 +6858,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/json5": { @@ -5414,14 +6874,6 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==" }, - "node_modules/@types/lodash.mergewith": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz", - "integrity": "sha512-RY/8IaVENjG19rxTZu9Nukqh0W2UrYgmBj5sdns4hWRZaV8PqR7wIKHFKzvOTjo4zVRV7sVI+yFhAJql12Kfqg==", - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -5429,9 +6881,9 @@ "dev": true }, "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "node_modules/@types/minimatch": { @@ -5444,7 +6896,7 @@ "version": "14.0.26", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.26.tgz", "integrity": "sha512-W+fpe5s91FBGE0pEa0lnqGLL4USgpLgs4nokw16SrBBco/gQxuua7KnArSEOd5iaMqbbSHV10vUDkJYJJqpXKA==", - "dev": true + "devOptional": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.0", @@ -5508,9 +6960,9 @@ } }, "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", "dev": true }, "node_modules/@types/scheduler": { @@ -5524,29 +6976,40 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "dependencies": { "@types/express": "*" } }, "node_modules/@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "dev": true, "dependencies": { - "@types/mime": "^1", - "@types/node": "*" + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" } }, "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "dependencies": { "@types/node": "*" @@ -5597,11 +7060,6 @@ "node": ">=0.10.0" } }, - "node_modules/@types/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==" - }, "node_modules/@types/webpack": { "version": "4.41.21", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.21.tgz", @@ -5645,6 +7103,15 @@ "node": ">=0.10.0" } }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "15.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", @@ -5658,7 +7125,7 @@ "version": "15.0.0", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", - "dev": true + "devOptional": true }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.62.0", @@ -5872,15 +7339,70 @@ "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" } }, - "node_modules/@visx/axis/node_modules/@visx/point": { + "node_modules/@visx/axis/node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@visx/axis/node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@visx/axis/node_modules/@visx/curve": { "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.17.0.tgz", - "integrity": "sha512-fUdGQBLGaSVbFTbQ6k+1nPisbqYjTjAdo9FhlwLd3W3uyXN/39Sx2z3N2579sVNBDzmCKdYNQIU0HC+/3Vqo6w==" + "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.17.0.tgz", + "integrity": "sha512-8Fw2ZalgYbpeoelLqTOmMs/wD8maSKsKS9rRIwmHZ0O0XxY8iG9oVYbD4CLWzf/uFWCY6+qofk4J1g9BWQSXJQ==", + "dependencies": { + "@types/d3-shape": "^1.3.1", + "d3-shape": "^1.0.6" + } + }, + "node_modules/@visx/axis/node_modules/@visx/scale": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.17.0.tgz", + "integrity": "sha512-ok0RUOSp9VxZzuwo/1I9nsxZxeAdU6wsvIb+cEyMrCuDwm79wzaioSkafAGSb39cKYNrGobFlA3vUd7+JZPCaw==", + "dependencies": { + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-time": "^2.0.0", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-time": "^2.1.1" + } + }, + "node_modules/@visx/axis/node_modules/@visx/shape": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.17.0.tgz", + "integrity": "sha512-c2uun6f9souLIyUx+WLetG2JSJ4hF3dJqs1yoFZuO5BLNcU35LTCbqvEq10hLPB7TLqkA0s3jWt/rpE4M3S0Mw==", + "dependencies": { + "@types/d3-path": "^1.0.8", + "@types/d3-shape": "^1.3.1", + "@types/lodash": "^4.14.172", + "@types/react": "*", + "@visx/curve": "2.17.0", + "@visx/group": "2.17.0", + "@visx/scale": "2.17.0", + "classnames": "^2.3.1", + "d3-path": "^1.0.5", + "d3-shape": "^1.2.0", + "lodash": "^4.17.21", + "prop-types": "^15.5.10" + }, + "peerDependencies": { + "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" + } }, "node_modules/@visx/bounds": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.10.0.tgz", - "integrity": "sha512-rY7WFTIjQaXA8tFL45O2qbtSRkyF4yF75HiWz06F7BVmJ9UjF2qlomB3Y1z6gk6ZiFhwQ4zxABjOVjAQPLn7nQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.17.0.tgz", + "integrity": "sha512-XsoyTAyCm+DZbrPgP3IZFZAcNqBmXFBLSep04TqnrEA3hf16GxIzcpaGe+hAVhPg5yzBdjc7tLk6s0h5F44niA==", "dependencies": { "@types/react": "*", "@types/react-dom": "*", @@ -5892,34 +7414,88 @@ } }, "node_modules/@visx/curve": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.17.0.tgz", - "integrity": "sha512-8Fw2ZalgYbpeoelLqTOmMs/wD8maSKsKS9rRIwmHZ0O0XxY8iG9oVYbD4CLWzf/uFWCY6+qofk4J1g9BWQSXJQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-3.3.0.tgz", + "integrity": "sha512-G1l1rzGWwIs8ka3mBhO/gj8uYK6XdU/3bwRSoiZ+MockMahQFPog0bUkuVgPwwzPSJfsA/E5u53Y/DNesnHQxg==", "dependencies": { "@types/d3-shape": "^1.3.1", "d3-shape": "^1.0.6" } }, "node_modules/@visx/event": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz", - "integrity": "sha512-WGp91g82s727g3NAnENF1ppC3ZAlvWg+Y+GG0WFg34NmmOZbvPI/PTOqTqZE3x6B8EUn8NJiMxRjxIMbi+IvRw==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.17.0.tgz", + "integrity": "sha512-fg2UWo89RgKgWWnnqI+i7EF8Ry+3CdMHTND4lo4DyJvcZZUCOwhxCHMQ4/PHW0EAUfxI51nGadcE1BcEVR5zWw==", "dependencies": { "@types/react": "*", - "@visx/point": "2.6.0" + "@visx/point": "2.17.0" + } + }, + "node_modules/@visx/glyph": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/glyph/-/glyph-3.3.0.tgz", + "integrity": "sha512-U2r1rFLpim3afKuuAmrbxXGSDCaLwXHmjXxWN8PiIQPMxpS7eaa/V5g2TRd/+x0KCkaf3Ismk4VKMl8ZlrmxIQ==", + "dependencies": { + "@types/d3-shape": "^1.3.1", + "@types/react": "*", + "@visx/group": "3.3.0", + "classnames": "^2.3.1", + "d3-shape": "^1.2.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, + "node_modules/@visx/glyph/node_modules/@visx/group": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/group/-/group-3.3.0.tgz", + "integrity": "sha512-yKepDKwJqlzvnvPS0yDuW13XNrYJE4xzT6xM7J++441nu6IybWWwextyap8ey+kU651cYDb+q1Oi6aHvQwyEyw==", + "dependencies": { + "@types/react": "*", + "classnames": "^2.3.1", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, + "node_modules/@visx/gradient": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/gradient/-/gradient-3.3.0.tgz", + "integrity": "sha512-t3vqukahDQsJ64/fcm85woFm2XPpSPMBz92gFvaY4J8EJY3e6rFOg382v5Dm17fgNsLRKJA0Vqo7mUtDe2pWOw==", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.5.7" + }, + "peerDependencies": { + "react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0" } }, "node_modules/@visx/grid": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/grid/-/grid-2.17.0.tgz", - "integrity": "sha512-lRs9PPx/QLFjQmXBiX42KXMbOTrywP0Os5v6D8sN+WSB72Gn4LDhVYeHqjFetZqk+ttDNbjE/Vecq+X6JWSCkQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/grid/-/grid-3.5.0.tgz", + "integrity": "sha512-i1pdobTE223ItMiER3q4ojIaZWja3vg46TkS6FotnBZ4c0VRDHSrALQPdi0na+YEgppASWCQ2WrI/vD6mIkhSg==", + "dependencies": { + "@types/react": "*", + "@visx/curve": "3.3.0", + "@visx/group": "3.3.0", + "@visx/point": "3.3.0", + "@visx/scale": "3.5.0", + "@visx/shape": "3.5.0", + "classnames": "^2.3.1", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, + "node_modules/@visx/grid/node_modules/@visx/group": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/group/-/group-3.3.0.tgz", + "integrity": "sha512-yKepDKwJqlzvnvPS0yDuW13XNrYJE4xzT6xM7J++441nu6IybWWwextyap8ey+kU651cYDb+q1Oi6aHvQwyEyw==", "dependencies": { "@types/react": "*", - "@visx/curve": "2.17.0", - "@visx/group": "2.17.0", - "@visx/point": "2.17.0", - "@visx/scale": "2.17.0", - "@visx/shape": "2.17.0", "classnames": "^2.3.1", "prop-types": "^15.6.2" }, @@ -5928,9 +7504,9 @@ } }, "node_modules/@visx/grid/node_modules/@visx/point": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.17.0.tgz", - "integrity": "sha512-fUdGQBLGaSVbFTbQ6k+1nPisbqYjTjAdo9FhlwLd3W3uyXN/39Sx2z3N2579sVNBDzmCKdYNQIU0HC+/3Vqo6w==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/point/-/point-3.3.0.tgz", + "integrity": "sha512-03eBBIJarkmX79WbeEGTUZwmS5/MUuabbiM9KfkGS9pETBTWkp1DZtEHZdp5z34x5TDQVLSi0rk1Plg3/8RtDg==" }, "node_modules/@visx/group": { "version": "2.17.0", @@ -5960,15 +7536,58 @@ "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" } }, + "node_modules/@visx/legend/node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@visx/legend/node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@visx/legend/node_modules/@visx/scale": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.17.0.tgz", + "integrity": "sha512-ok0RUOSp9VxZzuwo/1I9nsxZxeAdU6wsvIb+cEyMrCuDwm79wzaioSkafAGSb39cKYNrGobFlA3vUd7+JZPCaw==", + "dependencies": { + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-time": "^2.0.0", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-time": "^2.1.1" + } + }, + "node_modules/@visx/mock-data": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-3.3.0.tgz", + "integrity": "sha512-yb5R/tAU8fjwRSc5VL1UPYbkD+BoYjXUorblE3/oDcSfFrOvpRMZzSaYCBbZ6jtllge3Ks6QVzwyUUj1/xweqQ==", + "dependencies": { + "@types/d3-random": "^2.2.0", + "d3-random": "^2.2.2" + } + }, + "node_modules/@visx/mock-data/node_modules/d3-random": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-2.2.2.tgz", + "integrity": "sha512-0D9P8TRj6qDAtHhRQn6EfdOtHMfsUWanl3yb/84C4DqpZ+VsgfI5iTVRNRbELCfNvRfpMr8OrqqUTQ6ANGCijw==" + }, "node_modules/@visx/point": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.6.0.tgz", - "integrity": "sha512-amBi7yMz4S2VSchlPdliznN41TuES64506ySI22DeKQ+mc1s1+BudlpnY90sM1EIw4xnqbKmrghTTGfy6SVqvQ==" + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.17.0.tgz", + "integrity": "sha512-fUdGQBLGaSVbFTbQ6k+1nPisbqYjTjAdo9FhlwLd3W3uyXN/39Sx2z3N2579sVNBDzmCKdYNQIU0HC+/3Vqo6w==" }, "node_modules/@visx/responsive": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-2.10.0.tgz", - "integrity": "sha512-NssDPpuUYp7hqVISuYkKZ5zk6ob0++RdTIaUjRcUdyFEbvzb9+zIb8QToOkvI90L2EC/MY4Jx0NpDbEe79GpAw==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-2.17.0.tgz", + "integrity": "sha512-3dY2shGbQnoknIRv3Vfnwsy3ZA8Q5Q/rYnTLiokWChYRfNC8NMPoX9mprEeb/gMAxtKjaLn3zcCgd8R+eetxIQ==", "dependencies": { "@juggle/resize-observer": "^3.3.1", "@types/lodash": "^4.14.172", @@ -5981,30 +7600,25 @@ } }, "node_modules/@visx/scale": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.17.0.tgz", - "integrity": "sha512-ok0RUOSp9VxZzuwo/1I9nsxZxeAdU6wsvIb+cEyMrCuDwm79wzaioSkafAGSb39cKYNrGobFlA3vUd7+JZPCaw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-3.5.0.tgz", + "integrity": "sha512-xo3zrXV2IZxrMq9Y9RUVJUpd93h3NO/r/y3GVi5F9AsbOzOhsLIbsPkunhO9mpUSR8LZ9TiumLEBrY+3frRBSg==", "dependencies": { - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-time": "^2.0.0", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-time": "^2.1.1" + "@visx/vendor": "3.5.0" } }, "node_modules/@visx/shape": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.17.0.tgz", - "integrity": "sha512-c2uun6f9souLIyUx+WLetG2JSJ4hF3dJqs1yoFZuO5BLNcU35LTCbqvEq10hLPB7TLqkA0s3jWt/rpE4M3S0Mw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-3.5.0.tgz", + "integrity": "sha512-DP3t9jBQ7dSE3e6ptA1xO4QAIGxO55GrY/6P+S6YREuQGjZgq20TLYLAsiaoPEzFSS4tp0m12ZTPivWhU2VBTw==", "dependencies": { "@types/d3-path": "^1.0.8", "@types/d3-shape": "^1.3.1", "@types/lodash": "^4.14.172", "@types/react": "*", - "@visx/curve": "2.17.0", - "@visx/group": "2.17.0", - "@visx/scale": "2.17.0", + "@visx/curve": "3.3.0", + "@visx/group": "3.3.0", + "@visx/scale": "3.5.0", "classnames": "^2.3.1", "d3-path": "^1.0.5", "d3-shape": "^1.2.0", @@ -6015,6 +7629,19 @@ "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" } }, + "node_modules/@visx/shape/node_modules/@visx/group": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/group/-/group-3.3.0.tgz", + "integrity": "sha512-yKepDKwJqlzvnvPS0yDuW13XNrYJE4xzT6xM7J++441nu6IybWWwextyap8ey+kU651cYDb+q1Oi6aHvQwyEyw==", + "dependencies": { + "@types/react": "*", + "classnames": "^2.3.1", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, "node_modules/@visx/text": { "version": "2.17.0", "resolved": "https://registry.npmjs.org/@visx/text/-/text-2.17.0.tgz", @@ -6032,12 +7659,12 @@ } }, "node_modules/@visx/tooltip": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.10.0.tgz", - "integrity": "sha512-6Zrd79MIEfyuLBcZ1ypSeAkpQc8oLRNB7FQnegzl3Lje4LK5lJtuf5ST0mwK6G2Uv+GlOW9REJ6VK4gfAGkq9A==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.17.0.tgz", + "integrity": "sha512-+dMHURP9NqSFZLomMUnoVYjRs+I2qcOw1yYvLtTp/4GUAFRMSUJoSJeuLwng1VBIgCEB95xuQ95NgGID4qzPxA==", "dependencies": { "@types/react": "*", - "@visx/bounds": "2.10.0", + "@visx/bounds": "2.17.0", "classnames": "^2.3.1", "prop-types": "^15.5.10", "react-use-measure": "^2.0.4" @@ -6047,181 +7674,266 @@ "react-dom": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0" } }, + "node_modules/@visx/vendor": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/vendor/-/vendor-3.5.0.tgz", + "integrity": "sha512-yt3SEZRVmt36+APsCISSO9eSOtzQkBjt+QRxNRzcTWuzwMAaF3PHCCSe31++kkpgY9yFoF+Gfes1TBe5NlETiQ==", + "dependencies": { + "@types/d3-array": "3.0.3", + "@types/d3-color": "3.1.0", + "@types/d3-delaunay": "6.0.1", + "@types/d3-format": "3.0.1", + "@types/d3-geo": "3.1.0", + "@types/d3-interpolate": "3.0.1", + "@types/d3-scale": "4.0.2", + "@types/d3-time": "3.0.0", + "@types/d3-time-format": "2.1.0", + "d3-array": "3.2.1", + "d3-color": "3.1.0", + "d3-delaunay": "6.0.2", + "d3-format": "3.1.0", + "d3-geo": "3.1.0", + "d3-interpolate": "3.0.1", + "d3-scale": "4.0.2", + "d3-time": "3.1.0", + "d3-time-format": "4.1.0", + "internmap": "2.0.3" + } + }, + "node_modules/@visx/vendor/node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "node_modules/@visx/vendor/node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@visx/vendor/node_modules/@types/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@visx/vendor/node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "node_modules/@visx/vendor/node_modules/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@visx/vendor/node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, - "dependencies": { - "envinfo": "^7.7.3" + "license": "MIT", + "engines": { + "node": ">=14.15.0" }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -6289,6 +8001,24 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/@zag-js/dom-query": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.16.0.tgz", + "integrity": "sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ==" + }, + "node_modules/@zag-js/element-size": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.10.5.tgz", + "integrity": "sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w==" + }, + "node_modules/@zag-js/focus-visible": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.16.0.tgz", + "integrity": "sha512-a7U/HSopvQbrDU4GLerpqiMcHKEkQkNPeDZJWz38cw/6Upunh41GjHetq5TB84hxyCaDzJ6q2nEdNoBQfC0FKA==", + "dependencies": { + "@zag-js/dom-query": "0.16.0" + } + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -6314,9 +8044,9 @@ } }, "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -6347,13 +8077,16 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, + "engines": { + "node": ">=10.13.0" + }, "peerDependencies": { - "acorn": "^8" + "acorn": "^8.14.0" } }, "node_modules/acorn-jsx": { @@ -6516,6 +8249,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -6807,12 +8541,6 @@ "graphql": "^15.3.0 || ^16.0.0" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -6823,29 +8551,20 @@ } }, "node_modules/aria-hidden": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.1.tgz", - "integrity": "sha512-PN344VAf9j1EAi+jyVHOJ8XidQdPVssGco39eNcsGdM4wcsILtxrKLkbuiMfLWYROK1FjRQasMWCBttrhjnr6A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", "dependencies": { "tslib": "^2.0.0" }, "engines": { "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.9.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } } }, "node_modules/aria-hidden/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/aria-query": { "version": "5.0.0", @@ -6978,6 +8697,26 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", + "dev": true, + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/asn1js/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -7011,15 +8750,6 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -7032,6 +8762,21 @@ "node": ">= 4.5.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -7269,11 +9014,6 @@ "node": ">= 10.14.2" } }, - "node_modules/backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -7353,23 +9093,21 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "devOptional": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, - "node_modules/bcp-47": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", - "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", - "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -7406,22 +9144,22 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", + "bytes": "~3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -7437,51 +9175,50 @@ } }, "node_modules/body-parser/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/body-parser/node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/body-parser/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/bonjour-service": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.12.tgz", - "integrity": "sha512-pMmguXYCu63Ug37DluMKEHdxc+aaIf/ay4YbF8Gxtba+9d3u+rmEWy61VK3Z3hp8Rskok3BunHYnG0dUHAsblw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", "dev": true, "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.4" + "multicast-dns": "^7.2.5" } }, - "node_modules/bonjour-service/node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -7489,9 +9226,9 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", @@ -7499,12 +9236,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -7517,9 +9254,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "devOptional": true, "funding": [ { @@ -7529,13 +9266,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -7566,7 +9308,7 @@ "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "dev": true }, "node_modules/buffer-from": { @@ -7575,6 +9317,21 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -7583,6 +9340,15 @@ "node": ">= 0.8" } }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -7604,12 +9370,48 @@ } }, "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { @@ -7640,7 +9442,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=10" }, @@ -7649,9 +9451,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001375", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", + "version": "1.0.30001768", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001768.tgz", + "integrity": "sha512-qY3aDRZC5nWPgHUgIB84WL+nySuo19wk0VJpp/XI9T34lrvkyhRvNVOFJOp2kxClQhiFBu+TaUSudf6oa3vkSA==", "devOptional": true, "funding": [ { @@ -7661,6 +9463,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -7680,6 +9486,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -7698,12 +9505,6 @@ "node": ">=10" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "node_modules/chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", @@ -7849,9 +9650,9 @@ } }, "node_modules/cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, "dependencies": { "colors": "1.0.3" @@ -7860,15 +9661,6 @@ "node": ">= 0.2.0" } }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -7936,6 +9728,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -7943,7 +9736,13 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==" }, "node_modules/colorette": { "version": "2.0.16", @@ -7973,12 +9772,12 @@ } }, "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, "engines": { - "node": ">= 6" + "node": ">=14" } }, "node_modules/commondir": { @@ -7997,7 +9796,6 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -8006,36 +9804,53 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/compute-scroll-into-view": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz", - "integrity": "sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.0.3.tgz", + "integrity": "sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A==" }, "node_modules/concat-map": { "version": "0.0.1", @@ -8083,9 +9898,9 @@ ] }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -8099,9 +9914,10 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8127,34 +9943,121 @@ } }, "node_modules/copy-to-clipboard": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", - "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", "dependencies": { "toggle-selection": "^1.0.6" } }, - "node_modules/core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "node_modules/copy-webpack-plugin": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-14.0.0.tgz", + "integrity": "sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==", + "dev": true, + "dependencies": { + "glob-parent": "^6.0.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^7.0.3", + "tinyglobby": "^0.2.12" + }, + "engines": { + "node": ">= 20.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "dependencies": { - "browserslist": "^4.21.3", - "semver": "7.0.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://opencollective.com/webpack" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "node_modules/copy-webpack-plugin/node_modules/serialize-javascript": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, "node_modules/core-util-is": { @@ -8191,17 +10094,12 @@ "node": ">=10" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -8284,9 +10182,9 @@ "dev": true }, "node_modules/csstype": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", - "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/d3": { "version": "7.8.0", @@ -8740,12 +10638,14 @@ } }, "node_modules/date-fns": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", - "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "dev": true, - "engines": { - "node": ">=0.11" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/debounce": { @@ -8786,13 +10686,18 @@ } }, "node_modules/deeks": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/deeks/-/deeks-2.5.1.tgz", - "integrity": "sha512-fqrBeUz7f1UqaXDRzVB5RG2EfPk15HJRrb2pMZj8mLlSTtz4tRPsK5leFOskoHFPuyZ6+7aRM9j657fvXLkJ7Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.2.1.tgz", + "integrity": "sha512-D/o0k3pCG1aI1cxb/dDiWmtMc4Rh7ZQBybXpfMsw9Rbtqwg8kUA9SpYkWcw0pAUjZSnPm8MluctiS0o68r69jQ==", "engines": { - "node": ">= 12" + "node": ">= 16" } }, + "node_modules/deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -8807,86 +10712,34 @@ "node": ">=0.10.0" } }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/default-gateway/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-gateway/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-gateway/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/default-gateway/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "dev": true, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-gateway/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -8896,13 +10749,33 @@ "clone": "^1.0.2" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -9055,15 +10928,6 @@ "wrappy": "1" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", @@ -9085,16 +10949,10 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, "node_modules/dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" @@ -9104,11 +10962,11 @@ } }, "node_modules/doc-path": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-3.0.2.tgz", - "integrity": "sha512-VRlA2OKSjTbHWj6wmSanxJ338fE/YN8pqmZ0FIWK5JWkIJMFRc4KmD35JtOrnjvVG0WrzOtDDNHx1lN1tkb+lA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.4.tgz", + "integrity": "sha512-yw5D++UCIB6a033PvQaUvSpW2QuKW0+DOId763n0Q4z3brxS7G8oQr8yBQ1nQFkognKrAVrV6I55TLeU9cfXTg==", "engines": { - "node": ">=12" + "node": ">=16" } }, "node_modules/doctrine": { @@ -9245,14 +11103,33 @@ } }, "node_modules/dset": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", - "integrity": "sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", "dev": true, "engines": { "node": ">=4" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -9268,9 +11145,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.4.213", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.213.tgz", - "integrity": "sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg==", + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "devOptional": true }, "node_modules/emittery": { @@ -9315,13 +11192,13 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -9349,10 +11226,11 @@ } }, "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "dev": true, + "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -9410,12 +11288,54 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -9439,10 +11359,51 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" + } + }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "devOptional": true, "engines": { "node": ">=6" @@ -9457,6 +11418,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -9967,15 +11929,20 @@ "dev": true }, "node_modules/eslint/node_modules/debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/eslint/node_modules/escape-string-regexp": { @@ -10077,6 +12044,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -10179,7 +12157,7 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } @@ -10224,9 +12202,9 @@ } }, "node_modules/execa/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { "nice-try": "^1.0.4", @@ -10380,44 +12358,48 @@ "dev": true }, "node_modules/express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", - "content-disposition": "0.5.4", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/depd": { @@ -10428,26 +12410,14 @@ "node": ">= 0.8" } }, - "node_modules/express/node_modules/http-errors": { + "node_modules/express/node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } }, - "node_modules/express/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -10500,20 +12470,6 @@ "node": ">=0.10.0" } }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -10623,19 +12579,6 @@ "node": ">=8.6.0" } }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -10654,6 +12597,22 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", @@ -10690,18 +12649,6 @@ "bser": "2.1.1" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -10715,9 +12662,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -10727,12 +12674,13 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -10743,10 +12691,20 @@ "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/finalhandler/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -10828,18 +12786,6 @@ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -10869,15 +12815,15 @@ } }, "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "node_modules/focus-lock": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.2.tgz", - "integrity": "sha512-YtHxjX7a0IC0ZACL5wsX8QdncXofWpGPNoVMuI/nZUrPGp6LmNI6+D5j0pPj+v8Kw5EpweA+T5yImK0rnWf7oQ==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.5.tgz", + "integrity": "sha512-QFaHbhv9WPUeLYBDe/PAuLKJ4Dd9OPvKs9xZBr3yLXnUrDNaVXKu2baDBXe3naPY30hgHYSsf2JW4jzas2mDEQ==", "dependencies": { "tslib": "^2.0.3" }, @@ -10886,14 +12832,14 @@ } }, "node_modules/focus-lock/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true, "funding": [ { @@ -10910,6 +12856,21 @@ } } }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -10919,14 +12880,46 @@ "node": ">=0.10.0" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -10934,13 +12927,13 @@ } }, "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0", "qs": "^6.11.0" }, @@ -10948,21 +12941,6 @@ "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/formidable/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/formik": { "version": "2.2.9", "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz", @@ -11046,32 +13024,11 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } }, - "node_modules/fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, "node_modules/fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -11099,9 +13056,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -11136,12 +13096,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fuzzaldrin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", - "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=", - "dev": true - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -11161,13 +13115,26 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-nonce": { @@ -11187,6 +13154,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -11276,7 +13255,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=4" } @@ -11306,10 +13285,21 @@ "node": ">=0.10.0" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/graphql": { @@ -11400,26 +13390,27 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, "engines": { "node": ">=4" } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -11428,12 +13419,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -11505,6 +13496,17 @@ "node": ">=0.10.0" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -11514,33 +13516,11 @@ "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/hey-listen": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -11609,12 +13589,6 @@ "node": ">=10" } }, - "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "dev": true - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -11699,6 +13673,42 @@ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", "dev": true }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", @@ -11757,10 +13767,11 @@ "dev": true }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -11825,6 +13836,15 @@ "node": ">=8.12.0" } }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "engines": { + "node": ">=10.18" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -11858,15 +13878,18 @@ "dev": true }, "node_modules/import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/import-local": { @@ -11977,94 +14000,6 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -12088,12 +14023,27 @@ } }, "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" + } + }, + "node_modules/intro.js": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/intro.js/-/intro.js-7.2.0.tgz", + "integrity": "sha512-qbMfaB70rOXVBceIWNYnYTpVTiZsvQh/MIkfdQbpA9di9VBfj1GigUPfcCv3aOfsbrtPcri8vTLTA4FcEDcHSQ==" + }, + "node_modules/intro.js-react": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/intro.js-react/-/intro.js-react-1.0.0.tgz", + "integrity": "sha512-zR8pbTyX20RnCZpJMc0nuHBpsjcr1wFkj3ZookV6Ly4eE/LGpFTQwPsaA61Cryzwiy/tTFsusf4hPU9NpI9UOg==", + "peerDependencies": { + "intro.js": ">=2.5.0", + "react": ">=0.14.0" } }, "node_modules/invariant": { @@ -12136,22 +14086,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -12199,9 +14133,9 @@ "dev": true }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -12266,12 +14200,6 @@ "node": ">= 0.4" } }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, "node_modules/is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -12300,6 +14228,7 @@ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", "dev": true, + "optional": true, "bin": { "is-docker": "cli.js" }, @@ -12355,6 +14284,39 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -12364,6 +14326,11 @@ "node": ">=8" } }, + "node_modules/is-lite": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz", + "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw==" + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -12376,6 +14343,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-network-error": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", + "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -12400,6 +14379,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-observable": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz", + "integrity": "sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -12527,6 +14519,21 @@ "node": ">= 0.4" } }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -12559,6 +14566,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -12567,9 +14575,10 @@ } }, "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", @@ -12720,7 +14729,23 @@ "node_modules/iterall": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", - "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/jest": { "version": "26.6.3", @@ -14365,9 +16390,9 @@ } }, "node_modules/jest-worker": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.2.tgz", - "integrity": "sha512-0QMy/zPovLfUPyHuOuuU4E+kGACXXE84nRnq6lBVI9GJg5DCBiA97SATi+ZP8CpiJwEQy1oCPjRBf8AnLjN+Ag==", + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "dependencies": { "@types/node": "*", @@ -14402,15 +16427,30 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-sha256": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz", + "integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "dependencies": { "argparse": "^1.0.7", @@ -14466,40 +16506,49 @@ } } }, + "node_modules/jsdom/node_modules/form-data": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "devOptional": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-2-csv": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-3.17.1.tgz", - "integrity": "sha512-i6QynVy42GGMgY8fYde0mp6nYteptvk8oJsphOLiT3CITzw7NBBAiRwHV35kDOBii/elDQe1HCWLqaBPJ3istQ==", + "version": "5.5.11", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.11.tgz", + "integrity": "sha512-kVuwgVL7rfad9ETf02ZZxJPuMR5ZSUn139+T34BfmVxYhb/IsAIm/LzEeQ8YLJmXfuQ5z7LUAFrgPZZM6VLJPw==", "dependencies": { - "deeks": "2.5.1", - "doc-path": "3.0.2" + "deeks": "3.2.1", + "doc-path": "4.1.4" }, "engines": { - "node": ">= 12" + "node": ">= 16" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -14525,16 +16574,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -14586,23 +16625,23 @@ } }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dev": true, "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dev": true, "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -14624,11 +16663,21 @@ "node": ">=6" } }, + "node_modules/launch-editor": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz", + "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==", + "dev": true, + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -14652,12 +16701,16 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -14674,40 +16727,21 @@ "node": ">=8.9.0" } }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -14909,12 +16943,6 @@ "semver": "bin/semver.js" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/make-plural": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.1.0.tgz", @@ -14955,6 +16983,14 @@ "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz", "integrity": "sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -14964,26 +17000,504 @@ } }, "node_modules/memfs": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", - "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.2.tgz", + "integrity": "sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==", + "dev": true, + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-to-fsa": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-core": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.2.tgz", + "integrity": "sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==", "dev": true, "dependencies": { - "fs-monkey": "1.0.3" + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "thingies": "^2.5.0" }, "engines": { - "node": ">= 4.0.0" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + "node_modules/memfs/node_modules/@jsonjoy.com/fs-fsa": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.2.tgz", + "integrity": "sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==", + "dev": true, + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.2.tgz", + "integrity": "sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==", + "dev": true, + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", + "glob-to-regex.js": "^1.0.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node-builtins": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.2.tgz", + "integrity": "sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node-to-fsa": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.2.tgz", + "integrity": "sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==", + "dev": true, + "dependencies": { + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node-utils": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.2.tgz", + "integrity": "sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==", + "dev": true, + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.57.2" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-print": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.2.tgz", + "integrity": "sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==", + "dev": true, + "dependencies": { + "@jsonjoy.com/fs-node-utils": "4.57.2", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.2.tgz", + "integrity": "sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==", + "dev": true, + "dependencies": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "dev": true, + "dependencies": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "dev": true, + "dependencies": { + "@jsonjoy.com/util": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "dev": true, + "dependencies": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "dev": true, + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "dev": true, + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/thingies": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", + "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", + "dev": true, + "engines": { + "node": ">=10.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/memfs/node_modules/tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -15000,11 +17514,6 @@ "node": ">= 8" } }, - "node_modules/messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -15014,16 +17523,17 @@ } }, "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">=8.6" } }, "node_modules/mime": { @@ -15066,9 +17576,9 @@ } }, "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.1.tgz", + "integrity": "sha512-8lqe85PkqQJzIcs2iD7xW/WSxcncC3/DPVbTOafKNJDIMXwGfwXS350mH4SJslomntN2iYtFBuC0yNO3CEap6g==", "dev": true, "dependencies": { "dom-walk": "^0.1.0" @@ -15090,9 +17600,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -15107,6 +17617,16 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -15132,17 +17652,10 @@ "node": ">=0.10.0" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" }, "node_modules/mq-polyfill": { "version": "1.1.8", @@ -15171,12 +17684,6 @@ "multicast-dns": "cli.js" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, "node_modules/nanoclone": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", @@ -15290,24 +17797,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-gettext": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", - "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", - "dev": true, - "dependencies": { - "lodash.get": "^4.4.2" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -15355,22 +17844,21 @@ } }, "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "devOptional": true }, "node_modules/nodemon": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz", - "integrity": "sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==", + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", "dev": true, - "hasInstallScript": true, "dependencies": { "chokidar": "^3.5.2", "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", "semver": "^5.7.1", "simple-update-notifier": "^1.0.7", @@ -15547,9 +18035,13 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -15666,6 +18158,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/observable-fns": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz", + "integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==", + "dev": true, + "license": "MIT" + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -15684,10 +18183,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "engines": { "node": ">= 0.8" } @@ -15717,17 +18215,18 @@ } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -15842,15 +18341,6 @@ "node": ">=8" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -15881,18 +18371,6 @@ "node": ">=6" } }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/p-map": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", @@ -15903,16 +18381,20 @@ } }, "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", "dev": true, "dependencies": { - "@types/retry": "0.12.0", + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", "retry": "^0.13.1" }, "engines": { - "node": ">=8" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { @@ -15924,11 +18406,12 @@ "node": ">=6" } }, - "node_modules/papaparse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.0.tgz", - "integrity": "sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==", - "dev": true + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" }, "node_modules/param-case": { "version": "3.0.4", @@ -15958,17 +18441,20 @@ } }, "node_modules/parse-json": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz", - "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", + "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parse5": { @@ -16048,10 +18534,37 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" }, "node_modules/path-type": { "version": "4.0.0", @@ -16062,15 +18575,14 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "devOptional": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "engines": { "node": ">=8.6" @@ -16121,29 +18633,47 @@ "node": ">= 6" } }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", "dev": true, "dependencies": { - "find-up": "^3.0.0" + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/plurals-cldr": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/plurals-cldr/-/plurals-cldr-1.0.4.tgz", - "integrity": "sha512-4nLXqtel7fsCgzi8dvRZvUjfL8SXpP982sKg7b2TgpnR8rDnes06iuQ83trQ/+XdtyMIQkBBbKzX6x97eLfsJQ==", + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/pkijs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, "node_modules/pofile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.0.tgz", - "integrity": "sha512-6XYcNkXWGiJ2CVXogTP7uJ6ZXQCldYLZc16wgRp8tqRaBTTyIfF+TUT3EQJPXTLAT7OTPpTAoaFdoXKfaTRU1w==", - "dev": true + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.4.tgz", + "integrity": "sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g==", + "dev": true, + "license": "MIT" }, "node_modules/popmotion": { "version": "9.3.6", @@ -16161,6 +18691,16 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -16170,6 +18710,15 @@ "node": ">=0.10.0" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -16320,12 +18869,18 @@ } }, "node_modules/pseudolocale": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-1.2.0.tgz", - "integrity": "sha512-k0OQFvIlvpRdzR0dPVrrbWX7eE9EaZ6gpZtTlFSDi1Gf9tMy9wiANCNu7JZ0drcKgUri/39a2mBbH0goiQmrmQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-2.1.0.tgz", + "integrity": "sha512-af5fsrRvVwD+MBasBJvuDChT0KDqT0nEwD9NTgbtHJ16FKomWac9ua0z6YVNB4G9x9IOaiGWym62aby6n4tFMA==", "dev": true, "dependencies": { - "commander": "*" + "commander": "^10.0.0" + }, + "bin": { + "pseudolocale": "dist/cli.mjs" + }, + "engines": { + "node": ">=16.0.0" } }, "node_modules/psl": { @@ -16359,12 +18914,36 @@ "node": ">=6" } }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvtsutils/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -16399,21 +18978,6 @@ } ] }, - "node_modules/ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -16423,14 +18987,15 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" @@ -16440,34 +19005,42 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body/node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -16494,33 +19067,6 @@ "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-diff-viewer-continued": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-3.2.6.tgz", - "integrity": "sha512-GrzyqQnjIMoej+jMjWvtVSsQqhXgzEGqpXlJ2dAGfOk7Q26qcm8Gu6xtI430PBUyZsERe8BJSQf+7VZZo8IBNQ==", - "dependencies": { - "@emotion/css": "^11.10.5", - "classnames": "^2.3.1", - "diff": "^5.1.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">= 8" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-diff-viewer-continued/node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -16553,6 +19099,71 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, + "node_modules/react-floater": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz", + "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==", + "dependencies": { + "deepmerge": "^4.3.1", + "is-lite": "^0.8.2", + "popper.js": "^1.16.0", + "prop-types": "^15.8.1", + "tree-changes": "^0.9.1" + }, + "peerDependencies": { + "react": "15 - 18", + "react-dom": "15 - 18" + } + }, + "node_modules/react-floater/node_modules/@gilbarbara/deep-equal": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==" + }, + "node_modules/react-floater/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-floater/node_modules/is-lite": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz", + "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==" + }, + "node_modules/react-floater/node_modules/tree-changes": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz", + "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==", + "dependencies": { + "@gilbarbara/deep-equal": "^0.1.1", + "is-lite": "^0.8.2" + } + }, + "node_modules/react-focus-lock": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.12.1.tgz", + "integrity": "sha512-lfp8Dve4yJagkHiFrC1bGtib3mF2ktqwPJw4/WGcgPW+pJ/AVQA5X2vI7xgp13FcxFEpYBBHpXai/N2DBNC0Jw==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "focus-lock": "^1.3.5", + "prop-types": "^15.6.2", + "react-clientside-effect": "^1.2.6", + "use-callback-ref": "^1.3.2", + "use-sidecar": "^1.1.2" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-hot-loader": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.0.tgz", @@ -16607,11 +19218,61 @@ "node": ">= 8" } }, + "node_modules/react-innertext": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz", + "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==", + "peerDependencies": { + "@types/react": ">=0.0.0 <=99", + "react": ">=0.0.0 <=99" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-joyride": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.8.2.tgz", + "integrity": "sha512-2QY8HB1G0I2OT0PKMUz7gg2HAjdkG2Bqi13r0Bb1V16PAwfb9khn4wWBTOJsGsjulbAWiQ3/0YrgNUHGFmuifw==", + "dependencies": { + "@gilbarbara/deep-equal": "^0.3.1", + "deep-diff": "^1.0.2", + "deepmerge": "^4.3.1", + "is-lite": "^1.2.1", + "react-floater": "^0.7.9", + "react-innertext": "^1.1.5", + "react-is": "^16.13.1", + "scroll": "^3.0.1", + "scrollparent": "^2.1.0", + "tree-changes": "^0.11.2", + "type-fest": "^4.18.2" + }, + "peerDependencies": { + "react": "15 - 18", + "react-dom": "15 - 18" + } + }, + "node_modules/react-joyride/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-joyride/node_modules/type-fest": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.21.0.tgz", + "integrity": "sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -16635,10 +19296,34 @@ "react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.9.tgz", + "integrity": "sha512-bvHCLBrFfM2OgcrpPY2YW84sPdS2o2HKWJUf1xGyGLnSoEnOTOBpahIarjRuYtN0ryahCeP242yf+5TrBX/pZA==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.6", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-remove-scroll-bar": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.3.tgz", - "integrity": "sha512-i9GMNWwpz8XpUpQ6QlevUtFjHGqnPG4Hxs+wlIJntu/xcsZVEpJcIV71K3ZkqNy2q3GfgvkD7y6t/Sv8ofYSbw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", "dependencies": { "react-style-singleton": "^2.2.1", "tslib": "^2.0.0" @@ -16657,66 +19342,43 @@ } }, "node_modules/react-remove-scroll-bar/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/react-remove-scroll/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/react-router": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.3.tgz", - "integrity": "sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.4.tgz", + "integrity": "sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==", + "dependencies": { + "@remix-run/router": "1.23.3" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8" } }, "node_modules/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng==", + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz", + "integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==", "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.3", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.23.3", + "react-router": "6.30.4" }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router/node_modules/mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/react-router/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" + "react": ">=16.8", + "react-dom": ">=16.8" } }, "node_modules/react-shallow-renderer": { @@ -16755,9 +19417,9 @@ } }, "node_modules/react-style-singleton/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/react-table": { "version": "7.8.0", @@ -16830,15 +19492,16 @@ } }, "node_modules/rechoir": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", - "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, + "license": "MIT", "dependencies": { - "resolve": "^1.9.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/redent": { @@ -16877,6 +19540,12 @@ "balanced-match": "^1.0.0" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -16896,9 +19565,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.0", @@ -17116,11 +19785,6 @@ "node": ">=4" } }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -17194,13 +19858,16 @@ "node": "6.* || >= 7.*" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/run-parallel": { @@ -17231,18 +19898,6 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, - "node_modules/rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -17462,6 +20117,16 @@ "node": ">= 8.9.0" } }, + "node_modules/scroll": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz", + "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==" + }, + "node_modules/scrollparent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz", + "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA==" + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -17469,15 +20134,16 @@ "dev": true }, "node_modules/selfsigned": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", - "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", + "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", "dev": true, "dependencies": { - "node-forge": "^1" + "@peculiar/x509": "^1.14.2", + "pkijs": "^3.3.3" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/semver": { @@ -17490,9 +20156,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -17520,26 +20186,6 @@ "node": ">= 0.8" } }, - "node_modules/send/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -17553,15 +20199,6 @@ "node": ">= 0.8" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -17602,25 +20239,52 @@ "dev": true }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -17654,18 +20318,51 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sha.js/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/sha.js/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -17705,6 +20402,18 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", + "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -17713,13 +20422,72 @@ "optional": true }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -17732,9 +20500,9 @@ "dev": true }, "node_modules/simple-update-notifier": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz", - "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", "dev": true, "dependencies": { "semver": "~7.0.0" @@ -18110,12 +20878,20 @@ } }, "node_modules/spdy-transport/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/spdy-transport/node_modules/ms": { @@ -18125,12 +20901,20 @@ "dev": true }, "node_modules/spdy/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/spdy/node_modules/ms": { @@ -18254,6 +21038,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -18313,6 +21112,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -18383,34 +21195,6 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, - "node_modules/subscriptions-transport-ws": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", - "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "dependencies": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependencies": { - "graphql": "^15.7.2 || ^16.0.0" - } - }, - "node_modules/subscriptions-transport-ws/node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "node_modules/subscriptions-transport-ws/node_modules/symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/superagent": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.0.tgz", @@ -18450,20 +21234,6 @@ } } }, - "node_modules/superagent/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/superagent/node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -18514,6 +21284,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -18619,12 +21390,16 @@ "dev": true }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terminal-link": { @@ -18641,13 +21416,14 @@ } }, "node_modules/terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -18655,27 +21431,18 @@ }, "engines": { "node": ">=10" - }, - "peerDependencies": { - "acorn": "^8.5.0" - }, - "peerDependenciesMeta": { - "acorn": { - "optional": true - } } }, "node_modules/terser-webpack-plugin": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", - "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "dev": true, "dependencies": { - "jest-worker": "^27.0.6", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -18699,15 +21466,50 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -18717,30 +21519,12 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -18761,18 +21545,56 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/threads": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/threads/-/threads-1.7.0.tgz", + "integrity": "sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0", + "debug": "^4.2.0", + "is-observable": "^2.1.0", + "observable-fns": "^0.6.1" + }, + "funding": { + "url": "https://github.com/andywer/threads.js?sponsor=1" + }, + "optionalDependencies": { + "tiny-worker": ">= 2" + } + }, + "node_modules/threads/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/threads/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -18789,16 +21611,60 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/tiny-worker": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", + "integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "esm": "^3.2.25" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "dependencies": { - "os-tmpdir": "~1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=0.6.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/tmpl": { @@ -18807,14 +21673,40 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "node_modules/to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "dev": true, + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, + "node_modules/to-buffer/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -18869,7 +21761,7 @@ "node_modules/toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, "node_modules/toidentifier": { "version": "1.0.1", @@ -18932,6 +21824,15 @@ "node": ">=8" } }, + "node_modules/tree-changes": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.2.tgz", + "integrity": "sha512-4gXlUthrl+RabZw6lLvcCDl6KfJOCmrC16BC5CRdut1EAH509Omgg0BfKLY+ViRlzrvYOTWR0FMS2SQTwzumrw==", + "dependencies": { + "@gilbarbara/deep-equal": "^0.3.1", + "is-lite": "^1.2.0" + } + }, "node_modules/ts-invariant": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", @@ -18948,32 +21849,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, - "node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "typescript": ">=2.7" - } - }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -19018,6 +21893,18 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "dev": true, + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -19060,6 +21947,20 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -19070,17 +21971,18 @@ } }, "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true, + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -19164,15 +22066,6 @@ "node": ">=0.10.0" } }, - "node_modules/universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -19236,9 +22129,9 @@ "dev": true }, "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "devOptional": true, "funding": [ { @@ -19248,14 +22141,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -19296,9 +22193,9 @@ } }, "node_modules/use-callback-ref": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", - "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", "dependencies": { "tslib": "^2.0.0" }, @@ -19316,9 +22213,9 @@ } }, "node_modules/use-callback-ref/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/use-sidecar": { "version": "1.1.2", @@ -19342,9 +22239,9 @@ } }, "node_modules/use-sidecar/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -19414,11 +22311,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "node_modules/value-or-promise": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", @@ -19466,18 +22358,10 @@ "makeerror": "1.0.x" } }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -19515,35 +22399,36 @@ } }, "node_modules/webpack": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", - "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -19562,44 +22447,43 @@ } }, "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, + "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x" + "webpack": "5.x.x" }, "peerDependenciesMeta": { "@webpack-cli/generators": { "optional": true }, - "@webpack-cli/migrate": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -19608,15 +22492,6 @@ } } }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/webpack-config-utils": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/webpack-config-utils/-/webpack-config-utils-2.3.1.tgz", @@ -19635,38 +22510,44 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz", + "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==", "dev": true, "dependencies": { "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", + "memfs": "^4.43.1", + "mime-types": "^3.0.1", + "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } } }, "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -19691,19 +22572,44 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/webpack-dev-middleware/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -19711,79 +22617,96 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.10.0.tgz", - "integrity": "sha512-7dezwAs+k6yXVFZ+MaL8VnE+APobiO3zvpp3rBHe/HmWQ+avwh0Q3d0xxacOiBybZZ3syTZw9HXzpa3YNbAZDQ==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.5.tgz", + "integrity": "sha512-4wZtCquSuv9CKX8oybo+mqxtxZqWz47uM1Ch94lxowBztOhWCbhqvRbfC/mODOwxgV2brY+JGZpHq58/SuVFYg==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.25", + "@types/express-serve-static-core": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", "colorette": "^2.0.10", - "compression": "^1.7.4", + "compression": "^1.8.1", "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", + "express": "^4.22.1", "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.1", + "http-proxy-middleware": "^2.0.9", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^5.5.0", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" + "webpack": "^5.0.0" }, "peerDependenciesMeta": { + "webpack": { + "optional": true + }, "webpack-cli": { "optional": true } } }, - "node_modules/webpack-dev-server/node_modules/@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "node_modules/webpack-dev-server/node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "dependencies": { - "@types/node": "*" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/webpack-dev-server/node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -19803,16 +22726,10 @@ } }, "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -19825,14 +22742,17 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/webpack-dev-server/node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", + "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", "dev": true, "engines": { "node": ">= 10" @@ -19844,34 +22764,19 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/webpack-dev-server/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -19879,16 +22784,16 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", - "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -19913,23 +22818,58 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "engines": { "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/webpack/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -19940,9 +22880,9 @@ } }, "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz", + "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==", "dev": true, "dependencies": { "http-parser-js": ">=0.5.1", @@ -20028,6 +22968,27 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -20057,6 +23018,57 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -20106,9 +23118,10 @@ } }, "node_modules/ws": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", - "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "dev": true, "engines": { "node": ">=8.3.0" }, @@ -20125,6 +23138,36 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -20172,9 +23215,9 @@ "dev": true }, "node_modules/yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "engines": { "node": ">= 6" } @@ -20269,15 +23312,6 @@ "node": ">=8" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yup": { "version": "0.32.11", "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", @@ -20514,47 +23548,54 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" } }, "@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "devOptional": true }, "@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "devOptional": true, "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", - "convert-source-map": "^1.7.0", + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "devOptional": true + }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "devOptional": true, "requires": { "ms": "2.1.2" @@ -20575,26 +23616,24 @@ } }, "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "devOptional": true, + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "dependencies": { "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "devOptional": true, + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } } } @@ -20619,22 +23658,38 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "devOptional": true, "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "devOptional": true, + "requires": { + "yallist": "^3.0.2" + } + }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "devOptional": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "devOptional": true } } }, @@ -20704,7 +23759,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "devOptional": true + "dev": true }, "@babel/helper-explode-assignable-expression": { "version": "7.18.6", @@ -20719,20 +23774,16 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "devOptional": true, + "dev": true, "requires": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" } }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "devOptional": true, - "requires": { - "@babel/types": "^7.22.5" - } + "@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" }, "@babel/helper-member-expression-to-functions": { "version": "7.18.9", @@ -20744,27 +23795,23 @@ } }, "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "requires": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "devOptional": true, "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" } }, "@babel/helper-optimise-call-expression": { @@ -20777,9 +23824,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true }, "@babel/helper-remap-async-to-generator": { @@ -20808,12 +23855,13 @@ } }, "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", - "devOptional": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" } }, "@babel/helper-skip-transparent-expression-wrappers": { @@ -20829,25 +23877,25 @@ "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" }, "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==" }, "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "devOptional": true }, "@babel/helper-wrap-function": { @@ -20863,31 +23911,34 @@ } }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "devOptional": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" } }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "devOptional": true + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "requires": { + "@babel/types": "^7.29.0" + } }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -21393,16 +24444,15 @@ } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" } }, "@babel/plugin-transform-modules-umd": { @@ -21728,47 +24778,41 @@ } }, "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "devOptional": true, + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "devOptional": true, + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "dependencies": { "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "devOptional": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -21776,19 +24820,17 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" } }, "@bcoe/v8-coverage": { @@ -21798,701 +24840,1077 @@ "dev": true }, "@chakra-ui/accordion": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/accordion/-/accordion-1.4.11.tgz", - "integrity": "sha512-d/gvSgGwcZaJXxXqGmecpAgko/tUYb5vR0E0B2/V/z9AVbS8ei//fbiO9+8Ouyl/K46oWHWYj5vt8iTadlZleg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/accordion/-/accordion-2.3.1.tgz", + "integrity": "sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==", "requires": { - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/transition": "2.1.0" } }, "@chakra-ui/alert": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/alert/-/alert-1.3.7.tgz", - "integrity": "sha512-fFpJYBpHOIK/BX4BVl/xafYiDBUW+Bq/gUYDOo4iAiO4vHgxo74oa+yOwSRNlNjAgIX7pi2ridsYQALKyWyxxQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/alert/-/alert-2.2.2.tgz", + "integrity": "sha512-jHg4LYMRNOJH830ViLuicjb3F+v6iriE/2G5T+Sd0Hna04nukNJ1MxUmBPE+vI22me2dIflfelu2v9wdB6Pojw==", "requires": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/spinner": "2.1.0" } }, "@chakra-ui/anatomy": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-1.3.0.tgz", - "integrity": "sha512-vj/lcHkCuq/dtbl69DkNsftZTnrGEegB90ODs1B6rxw8iVMdDSYkthPPFAkqzNs4ppv1y2IBjELuVzpeta1OHA==", - "requires": { - "@chakra-ui/theme-tools": "^1.3.6" - } + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz", + "integrity": "sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==" }, "@chakra-ui/avatar": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/avatar/-/avatar-1.3.11.tgz", - "integrity": "sha512-/eRRK48Er92/QWAfWhxsJIN0gZBBvk+ew4Hglo+pxt3/NDnfTF2yPE7ZN29Dl6daPNbyTOpoksMwaU2mZIqLgA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/avatar/-/avatar-2.3.0.tgz", + "integrity": "sha512-8gKSyLfygnaotbJbDMHDiJoF38OHXUYVme4gGxZ1fLnQEdPVEaIWfH+NndIjOM0z8S+YEFnT9KyGMUtvPrBk3g==", "requires": { - "@chakra-ui/image": "1.1.10", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/image": "2.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/breadcrumb": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/breadcrumb/-/breadcrumb-1.3.6.tgz", - "integrity": "sha512-iXxienBO6RUnJEcDvyDWyRt+mzPyl7/b6N8i0vrjGKGLpgtayJFvIdo33tFcvx6TCy7V9hiE3HTtZnNomWdR6A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/breadcrumb/-/breadcrumb-2.2.0.tgz", + "integrity": "sha512-4cWCG24flYBxjruRi4RJREWTGF74L/KzI2CognAW/d/zWR0CjiScuJhf37Am3LFbCySP6WSoyBOtTIoTA4yLEA==", + "requires": { + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + } + }, + "@chakra-ui/breakpoint-utils": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.8.tgz", + "integrity": "sha512-Pq32MlEX9fwb5j5xx8s18zJMARNHlQZH2VH1RZgfgRDpp7DcEgtRW5AInfN5CfqdHLO1dGxA7I3MqEuL5JnIsA==", "requires": { - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/button": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/button/-/button-1.5.10.tgz", - "integrity": "sha512-IVEOrleI378CckAa3b3CTUHMPZRfpy6LPwn1Mx3sMpHEkDTKu8zJcjgEvCE8HYzNC1KbwBsa1PfTgk40ui6EtA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/button/-/button-2.1.0.tgz", + "integrity": "sha512-95CplwlRKmmUXkdEp/21VkEWgnwcx2TOBG6NfYlsuLBDHSLlo5FKIiE2oSi4zXc4TLcopGcWPNcm/NDaSC5pvA==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/spinner": "1.2.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/spinner": "2.1.0" } }, - "@chakra-ui/checkbox": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/checkbox/-/checkbox-1.7.1.tgz", - "integrity": "sha512-9Io97yn8OrdaIynCj+3Z/neJV7lTT1MtcdYh3BKMd7WnoJDkRY/GlBM8zsdgC5Wvm+ZQ1M83t0YvRPKLLzusyA==", + "@chakra-ui/card": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/card/-/card-2.2.0.tgz", + "integrity": "sha512-xUB/k5MURj4CtPAhdSoXZidUbm8j3hci9vnc+eZJVDqhDOShNlD6QeniQNRPRys4lWAQLCbFcrwL29C8naDi6g==", "requires": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/shared-utils": "2.0.5" + } + }, + "@chakra-ui/checkbox": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/checkbox/-/checkbox-2.3.2.tgz", + "integrity": "sha512-85g38JIXMEv6M+AcyIGLh7igNtfpAN6KGQFYxY9tBj0eWvWk4NKQxvqqyVta0bSAyIl1rixNIIezNpNWk2iO4g==", + "requires": { + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/visually-hidden": "2.2.0", + "@zag-js/focus-visible": "0.16.0" } }, "@chakra-ui/clickable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/clickable/-/clickable-1.2.6.tgz", - "integrity": "sha512-89SsrQwwwAadcl/bN8nZqqaaVhVNFdBXqQnxVy1t07DL5ezubmNb5SgFh9LDznkm9YYPQhaGr3W6HFro7iAHMg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/clickable/-/clickable-2.1.0.tgz", + "integrity": "sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw==", "requires": { - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/close-button": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/close-button/-/close-button-1.2.7.tgz", - "integrity": "sha512-cYTxfgrIlPU4IZm1sehZXxx/TNQBk9c3LBPvTpywEM8GVRGINh4YLq8WiMaPtO+TDNBnKoWS/jS4IHnR+abADw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/close-button/-/close-button-2.1.1.tgz", + "integrity": "sha512-gnpENKOanKexswSVpVz7ojZEALl2x5qjLYNqSQGbxz+aP9sOXPfUS56ebyBrre7T7exuWGiFeRwnM0oVeGPaiw==", "requires": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/icon": "3.2.0" } }, "@chakra-ui/color-mode": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-1.4.8.tgz", - "integrity": "sha512-iD4126DVQi06c6ARr3uf3R2rtEu8aBVjW8rhZ+lOsV26Z15iCJA7OAut13Xu06fcZvgjSB/ChDy6Sx9sV9UjHA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz", + "integrity": "sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" } }, "@chakra-ui/control-box": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/control-box/-/control-box-1.1.6.tgz", - "integrity": "sha512-EUcq5f854puG6ZA6wAWl4107OPl8+bj4MMHJCa48BB0qec0U8HCEtxQGnFwJmaYLalIAjMfHuY3OwO2A3Hi9hA==", - "requires": { - "@chakra-ui/utils": "1.10.4" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/control-box/-/control-box-2.1.0.tgz", + "integrity": "sha512-gVrRDyXFdMd8E7rulL0SKeoljkLQiPITFnsyMO8EFHNZ+AHt5wK4LIguYVEq88APqAGZGfHFWXr79RYrNiE3Mg==", + "requires": {} }, "@chakra-ui/counter": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-1.2.10.tgz", - "integrity": "sha512-HQd09IuJ4z8M8vWajH+99jBWWSHDesQZmnN95jUg3HKOuNleLaipf2JFdrqbO1uWQyHobn2PM6u+B+JCAh2nig==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-2.1.0.tgz", + "integrity": "sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/number-utils": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/css-reset": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.3.tgz", - "integrity": "sha512-AgfrE7bRTJvNi/4zIfacI/kBHmHmHEIeQtHwCvk/0qM9V2gK1VM3ctYlnibf7BTh17F/UszweOGRb1lHSPfWjw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-2.3.0.tgz", + "integrity": "sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==", "requires": {} }, "@chakra-ui/descendant": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/descendant/-/descendant-2.1.3.tgz", - "integrity": "sha512-aNYNv99gEPENCdw2N5y3FvL5wgBVcLiOzJ2TxSwb4EVYszbgBZ8Ry1pf7lkoSfysdxD0scgy2cVyxO8TsYTU4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/descendant/-/descendant-3.1.0.tgz", + "integrity": "sha512-VxCIAir08g5w27klLyi7PVo8BxhW4tgU/lxQyujkmi4zx7hT9ZdrcQLAted/dAa+aSIZ14S1oV0Q9lGjsAdxUQ==", "requires": { - "@chakra-ui/react-utils": "^1.2.3" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0" } }, + "@chakra-ui/dom-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/dom-utils/-/dom-utils-2.1.0.tgz", + "integrity": "sha512-ZmF2qRa1QZ0CMLU8M1zCfmw29DmPNtfjR9iTo74U5FPr3i1aoAh7fbJ4qAlZ197Xw9eAW28tvzQuoVWeL5C7fQ==" + }, "@chakra-ui/editable": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@chakra-ui/editable/-/editable-1.4.2.tgz", - "integrity": "sha512-a5zKghA/IvG7yNkmFl7Z9c2KSsf0FgyijsNPTg/4S5jxyz13QJtoTg40tdpyaxHHCT25y25iUcV4FYCj6Jd01w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/editable/-/editable-3.1.0.tgz", + "integrity": "sha512-j2JLrUL9wgg4YA6jLlbU88370eCRyor7DZQD9lzpY95tSOXpTljeg3uF9eOmDnCs6fxp3zDWIfkgMm/ExhcGTg==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, + "@chakra-ui/event-utils": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@chakra-ui/event-utils/-/event-utils-2.0.8.tgz", + "integrity": "sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw==" + }, "@chakra-ui/focus-lock": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/focus-lock/-/focus-lock-1.2.6.tgz", - "integrity": "sha512-ZJNE1oNdUM1aGWuCJ+bxFa/d3EwxzfMWzTKzSvKDK50GWoUQQ10xFTT9nY/yFpkcwhBvx1KavxKf44mIhIbSog==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/focus-lock/-/focus-lock-2.1.0.tgz", + "integrity": "sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==", "requires": { - "@chakra-ui/utils": "1.10.4", - "react-focus-lock": "2.5.2" - }, - "dependencies": { - "react-focus-lock": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.5.2.tgz", - "integrity": "sha512-WzpdOnEqjf+/A3EH9opMZWauag7gV0BxFl+EY4ElA4qFqYsUsBLnmo2sELbN5OC30S16GAWMy16B9DLPpdJKAQ==", - "requires": { - "@babel/runtime": "^7.0.0", - "focus-lock": "^0.9.1", - "prop-types": "^15.6.2", - "react-clientside-effect": "^1.2.5", - "use-callback-ref": "^1.2.5", - "use-sidecar": "^1.0.5" - } - } + "@chakra-ui/dom-utils": "2.1.0", + "react-focus-lock": "^2.9.4" } }, "@chakra-ui/form-control": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-1.6.0.tgz", - "integrity": "sha512-MtUE98aocP2QTgvyyJ/ABuG33mhT3Ox56phKreG3HzbUKByMwrbQSm1QcAgyYdqSZ9eKB2tXx+qgGNh+avAfDA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-2.2.0.tgz", + "integrity": "sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/hooks": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-1.9.1.tgz", - "integrity": "sha512-SEeh1alDKzrP9gMLWMnXOUDBQDKF/URL6iTmkumTn6vhawWNla6sPrcMyoCzWdMzwUhZp3QNtCKbUm7dxBXvPw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-2.2.1.tgz", + "integrity": "sha512-RQbTnzl6b1tBjbDPf9zGRo9rf/pQMholsOudTxjy4i9GfTfz6kgp5ValGjQm2z7ng6Z31N1cnjZ1AlSzQ//ZfQ==", "requires": { - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "compute-scroll-into-view": "1.0.14", - "copy-to-clipboard": "3.3.1" + "@chakra-ui/react-utils": "2.0.12", + "@chakra-ui/utils": "2.0.15", + "compute-scroll-into-view": "3.0.3", + "copy-to-clipboard": "3.3.3" + }, + "dependencies": { + "@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "requires": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "requires": { + "@types/lodash": "*" + } + }, + "framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "requires": { + "tslib": "2.4.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } } }, "@chakra-ui/icon": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-2.0.5.tgz", - "integrity": "sha512-ZrqRvCCIxGr4qFd/r1pmtd9tobRmv8KAxV7ygFoc/t4vOSKTcVIjhE12gsI3FzgvXM15ZFVwsxa1zodwgo5neQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-3.2.0.tgz", + "integrity": "sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==", "requires": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/icons": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-1.1.7.tgz", - "integrity": "sha512-YIHxey/B4M2PyFASlHXtAWFyW+tsAtGAChOJ8dsM2kpu1MbVUqm/6nMI1KIFd7Te5IWuNYA75rAHBdLI0Yu61A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.1.1.tgz", + "integrity": "sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g==", "requires": { - "@chakra-ui/icon": "2.0.5", - "@types/react": "^17.0.15" + "@chakra-ui/icon": "3.2.0" } }, "@chakra-ui/image": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-1.1.10.tgz", - "integrity": "sha512-PJZmhQ/R1PgdMyCRjALfoyq1FNh/WzMAw70sliHLtLcb9hBXniwQZuckYfUshCkUoFBj/ow9d4byn9Culdpk7Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-2.1.0.tgz", + "integrity": "sha512-bskumBYKLiLMySIWDGcz0+D9Th0jPvmX6xnRMs4o92tT3Od/bW26lahmV2a2Op2ItXeCmRMY+XxJH5Gy1i46VA==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/input": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/input/-/input-1.4.6.tgz", - "integrity": "sha512-Ljy/NbOhh9cNQxKTWQRsT4aQiXs2vVya+Cj5NpMAz08NFFjPZovsTawhI7m6ejT5Vsh76QYjh2rOLLI3fWqQQw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/input/-/input-2.1.2.tgz", + "integrity": "sha512-GiBbb3EqAA8Ph43yGa6Mc+kUPjh4Spmxp1Pkelr8qtudpc3p2PJOOebLpd90mcqw8UePPa+l6YhhPtp6o0irhw==", "requires": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/layout": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/layout/-/layout-1.8.0.tgz", - "integrity": "sha512-GJtEKez5AZu0XQTxI6a6jwA/hMDD36pP0HBxBOGuHP1hWCebDzMjraiMfWiP9w7hKERFE4j19kocHxIXyocfJA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/layout/-/layout-2.3.1.tgz", + "integrity": "sha512-nXuZ6WRbq0WdgnRgLw+QuxWAHuhDtVX8ElWqcTK+cSMFg/52eVP47czYBE5F35YhnoW2XBwfNoNgZ7+e8Z01Rg==", "requires": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/breakpoint-utils": "2.0.8", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, + "@chakra-ui/lazy-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@chakra-ui/lazy-utils/-/lazy-utils-2.0.5.tgz", + "integrity": "sha512-UULqw7FBvcckQk2n3iPO56TMJvDsNv0FKZI6PlUNJVaGsPbsYxK/8IQ60vZgaTVPtVcjY6BE+y6zg8u9HOqpyg==" + }, "@chakra-ui/live-region": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/live-region/-/live-region-1.1.6.tgz", - "integrity": "sha512-9gPQHXf7oW0jXyT5R/JzyDMfJ3hF70TqhN8bRH4fMyfNr2Se+SjztMBqCrv5FS5rPjcCeua+e0eArpoB3ROuWQ==", - "requires": { - "@chakra-ui/utils": "1.10.4" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/live-region/-/live-region-2.1.0.tgz", + "integrity": "sha512-ZOxFXwtaLIsXjqnszYYrVuswBhnIHHP+XIgK1vC6DePKtyK590Wg+0J0slDwThUAd4MSSIUa/nNX84x1GMphWw==", + "requires": {} }, "@chakra-ui/media-query": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@chakra-ui/media-query/-/media-query-2.0.4.tgz", - "integrity": "sha512-kn6g/L0IFFUHz2v4yiCsBnhg9jUeA7525Z+AWl+BPtvryi7i9J+AJ27y/QAge7vUGy4dwDeFyxOZTs2oZ9/BsA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/media-query/-/media-query-3.3.0.tgz", + "integrity": "sha512-IsTGgFLoICVoPRp9ykOgqmdMotJG0CnPsKvGQeSFOB/dZfIujdVb14TYxDU4+MURXry1MhJ7LzZhv+Ml7cr8/g==", "requires": { - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/breakpoint-utils": "2.0.8", + "@chakra-ui/react-env": "3.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/menu": { - "version": "1.8.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/menu/-/menu-1.8.11.tgz", - "integrity": "sha512-8K65xItPsdMvSfuGWYIGigOF/QMcy7+D48UIEO/Hu0u0ckd11/JXbpSIFPddH5fYedclJ18PGRohTne487OVjQ==", - "requires": { - "@chakra-ui/clickable": "1.2.6", - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/menu/-/menu-2.2.1.tgz", + "integrity": "sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g==", + "requires": { + "@chakra-ui/clickable": "2.1.0", + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/lazy-utils": "2.0.5", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-animation-state": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-disclosure": "2.1.0", + "@chakra-ui/react-use-focus-effect": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-outside-click": "2.2.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/transition": "2.1.0" } }, "@chakra-ui/modal": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/modal/-/modal-1.11.1.tgz", - "integrity": "sha512-B2BBDonHb04vbPLAWgko1JYBwgW8ZNSLyhTJK+rbrCsRSgazuLTcwq4hdyJqrYNWtaQEfSwpAXqJ7joMZdv59A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/modal/-/modal-2.3.1.tgz", + "integrity": "sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==", "requires": { - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/focus-lock": "1.2.6", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.4.1" - }, - "dependencies": { - "react-remove-scroll": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.4.1.tgz", - "integrity": "sha512-K7XZySEzOHMTq7dDwcHsZA6Y7/1uX5RsWhRXVYv8rdh+y9Qz2nMwl9RX/Mwnj/j7JstCGmxyfyC0zbVGXYh3mA==", - "requires": { - "react-remove-scroll-bar": "^2.1.0", - "react-style-singleton": "^2.1.0", - "tslib": "^1.0.0", - "use-callback-ref": "^1.2.3", - "use-sidecar": "^1.0.1" - } - } + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/focus-lock": "2.1.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/transition": "2.1.0", + "aria-hidden": "^1.2.3", + "react-remove-scroll": "^2.5.6" } }, "@chakra-ui/number-input": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-1.4.7.tgz", - "integrity": "sha512-LorGRZFMipom8vCUEbLi2s7bTHF2Fgiu766W0jTbzMje+8Z1ZoRQunH9OZWQnxnWQTUfUM2KBW8KwToYh1ojfQ==", - "requires": { - "@chakra-ui/counter": "1.2.10", - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" - } + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-2.1.2.tgz", + "integrity": "sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA==", + "requires": { + "@chakra-ui/counter": "2.1.0", + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-interval": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + } + }, + "@chakra-ui/number-utils": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-utils/-/number-utils-2.0.7.tgz", + "integrity": "sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg==" + }, + "@chakra-ui/object-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz", + "integrity": "sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ==" }, "@chakra-ui/pin-input": { - "version": "1.7.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/pin-input/-/pin-input-1.7.10.tgz", - "integrity": "sha512-Uz5vFK+ZevQtdYHBkddSFCrY44bweXLanpSv9X/D0pWpdML09qfPiKX4ydGzfRoS2u4L8NUtN86IcvdOQLhHQg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/pin-input/-/pin-input-2.1.0.tgz", + "integrity": "sha512-x4vBqLStDxJFMt+jdAHHS8jbh294O53CPQJoL4g228P513rHylV/uPscYUHrVJXRxsHfRztQO9k45jjTYaPRMw==", "requires": { - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/popover": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/@chakra-ui/popover/-/popover-1.11.9.tgz", - "integrity": "sha512-hJ1/Lwukox3ryTN7W1wnj+nE44utfLwQYvfUSdatt5dznnh8k0P6Wx7Hmjm1cYffRavBhqzwua/QZDWjJN9N0g==", - "requires": { - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/popover/-/popover-2.2.1.tgz", + "integrity": "sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==", + "requires": { + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/lazy-utils": "2.0.5", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-animation-state": "2.1.0", + "@chakra-ui/react-use-disclosure": "2.1.0", + "@chakra-ui/react-use-focus-effect": "2.1.0", + "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/popper": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/popper/-/popper-2.4.3.tgz", - "integrity": "sha512-TGzFnYt3mtIVkIejtYIAu4Ka9DaYLzMR4NgcqI6EtaTvgK7Xep+6RTiY/Nq+ZT3l/eaNUwqHRFoNrDUg1XYasA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/popper/-/popper-3.1.0.tgz", + "integrity": "sha512-ciDdpdYbeFG7og6/6J8lkTFxsSvwTdMLFkpVylAF6VNC22jssiWfquj2eyD4rJnzkRFPvIWJq8hvbfhsm+AjSg==", "requires": { - "@chakra-ui/react-utils": "1.2.3", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", "@popperjs/core": "^2.9.3" } }, "@chakra-ui/portal": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/portal/-/portal-1.3.10.tgz", - "integrity": "sha512-t2KQ6MXbyf1qFYxWw/bs//CnwD+Clq7mbsP1Y7g+THCz2FvlLlMj45BWocLB30NoNyA8WCS2zyMBszW2/qvDiA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/portal/-/portal-2.1.0.tgz", + "integrity": "sha512-9q9KWf6SArEcIq1gGofNcFPSWEyl+MfJjEUg/un1SMlQjaROOh3zYr+6JAwvcORiX7tyHosnmWC3d3wI2aPSQg==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" } }, "@chakra-ui/progress": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/progress/-/progress-1.2.6.tgz", - "integrity": "sha512-thaHRIYTVktgV78vJMNwzfCX+ickhSpn2bun6FtGVUphFx4tjV+ggz+IGohm6AH2hapskoR1mQU2iNZb6BK0hQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/progress/-/progress-2.2.0.tgz", + "integrity": "sha512-qUXuKbuhN60EzDD9mHR7B67D7p/ZqNS2Aze4Pbl1qGGZfulPW0PY8Rof32qDtttDQBkzQIzFGE8d9QpAemToIQ==", "requires": { - "@chakra-ui/theme-tools": "1.3.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0" } }, "@chakra-ui/provider": { - "version": "1.7.14", - "resolved": "https://registry.npmjs.org/@chakra-ui/provider/-/provider-1.7.14.tgz", - "integrity": "sha512-FCA33CZy/jFzExglKMioeri8sr9NtDTcNVPnx95ZJiA7WpfFo0xuZ6/fMC4DwIQPkJKbSIZBXYLZ3U10Ntylrw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/provider/-/provider-2.4.2.tgz", + "integrity": "sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==", "requires": { - "@chakra-ui/css-reset": "1.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/system": "1.12.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/css-reset": "2.3.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-env": "3.1.0", + "@chakra-ui/system": "2.6.2", + "@chakra-ui/utils": "2.0.15" + }, + "dependencies": { + "@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "requires": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "requires": { + "@types/lodash": "*" + } + }, + "framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "requires": { + "tslib": "2.4.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } } }, "@chakra-ui/radio": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/radio/-/radio-1.5.1.tgz", - "integrity": "sha512-zO5eShz+j68A7935jJ2q5u3brX/bjPEGh9Pj2+bnKbmC9Vva6jEzBSJsAx9n4WbkAzR3xDMGWsbpivFp8X1tJw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/radio/-/radio-2.1.2.tgz", + "integrity": "sha512-n10M46wJrMGbonaghvSRnZ9ToTv/q76Szz284gv4QUWvyljQACcGrXIONUnQ3BIwbOfkRqSk7Xl/JgZtVfll+w==", "requires": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@zag-js/focus-visible": "0.16.0" } }, "@chakra-ui/react": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-1.8.8.tgz", - "integrity": "sha512-/XqL25J0i0h+usAXBngn/RTG2u1oQRzbhHe9tNHwFyNbx/izIADhQW/6ji06QU0KtaRIU77XvgSAyTtMJY1KmA==", - "requires": { - "@chakra-ui/accordion": "1.4.11", - "@chakra-ui/alert": "1.3.7", - "@chakra-ui/avatar": "1.3.11", - "@chakra-ui/breadcrumb": "1.3.6", - "@chakra-ui/button": "1.5.10", - "@chakra-ui/checkbox": "1.7.1", - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/control-box": "1.1.6", - "@chakra-ui/counter": "1.2.10", - "@chakra-ui/css-reset": "1.1.3", - "@chakra-ui/editable": "1.4.2", - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/image": "1.1.10", - "@chakra-ui/input": "1.4.6", - "@chakra-ui/layout": "1.8.0", - "@chakra-ui/live-region": "1.1.6", - "@chakra-ui/media-query": "2.0.4", - "@chakra-ui/menu": "1.8.11", - "@chakra-ui/modal": "1.11.1", - "@chakra-ui/number-input": "1.4.7", - "@chakra-ui/pin-input": "1.7.10", - "@chakra-ui/popover": "1.11.9", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/progress": "1.2.6", - "@chakra-ui/provider": "1.7.14", - "@chakra-ui/radio": "1.5.1", - "@chakra-ui/react-env": "1.1.6", - "@chakra-ui/select": "1.2.11", - "@chakra-ui/skeleton": "1.2.14", - "@chakra-ui/slider": "1.5.11", - "@chakra-ui/spinner": "1.2.6", - "@chakra-ui/stat": "1.2.7", - "@chakra-ui/switch": "1.3.10", - "@chakra-ui/system": "1.12.1", - "@chakra-ui/table": "1.3.6", - "@chakra-ui/tabs": "1.6.10", - "@chakra-ui/tag": "1.2.7", - "@chakra-ui/textarea": "1.2.11", - "@chakra-ui/theme": "1.14.1", - "@chakra-ui/toast": "1.5.9", - "@chakra-ui/tooltip": "1.5.1", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.8.2.tgz", + "integrity": "sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==", + "requires": { + "@chakra-ui/accordion": "2.3.1", + "@chakra-ui/alert": "2.2.2", + "@chakra-ui/avatar": "2.3.0", + "@chakra-ui/breadcrumb": "2.2.0", + "@chakra-ui/button": "2.1.0", + "@chakra-ui/card": "2.2.0", + "@chakra-ui/checkbox": "2.3.2", + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/control-box": "2.1.0", + "@chakra-ui/counter": "2.1.0", + "@chakra-ui/css-reset": "2.3.0", + "@chakra-ui/editable": "3.1.0", + "@chakra-ui/focus-lock": "2.1.0", + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/hooks": "2.2.1", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/image": "2.1.0", + "@chakra-ui/input": "2.1.2", + "@chakra-ui/layout": "2.3.1", + "@chakra-ui/live-region": "2.1.0", + "@chakra-ui/media-query": "3.3.0", + "@chakra-ui/menu": "2.2.1", + "@chakra-ui/modal": "2.3.1", + "@chakra-ui/number-input": "2.1.2", + "@chakra-ui/pin-input": "2.1.0", + "@chakra-ui/popover": "2.2.1", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/progress": "2.2.0", + "@chakra-ui/provider": "2.4.2", + "@chakra-ui/radio": "2.1.2", + "@chakra-ui/react-env": "3.1.0", + "@chakra-ui/select": "2.1.2", + "@chakra-ui/skeleton": "2.1.0", + "@chakra-ui/skip-nav": "2.1.0", + "@chakra-ui/slider": "2.1.0", + "@chakra-ui/spinner": "2.1.0", + "@chakra-ui/stat": "2.1.1", + "@chakra-ui/stepper": "2.3.1", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/switch": "2.1.2", + "@chakra-ui/system": "2.6.2", + "@chakra-ui/table": "2.1.0", + "@chakra-ui/tabs": "3.0.0", + "@chakra-ui/tag": "3.1.1", + "@chakra-ui/textarea": "2.1.2", + "@chakra-ui/theme": "3.3.1", + "@chakra-ui/theme-utils": "2.0.21", + "@chakra-ui/toast": "7.0.2", + "@chakra-ui/tooltip": "2.3.1", + "@chakra-ui/transition": "2.1.0", + "@chakra-ui/utils": "2.0.15", + "@chakra-ui/visually-hidden": "2.2.0" + }, + "dependencies": { + "@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "requires": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "requires": { + "@types/lodash": "*" + } + }, + "framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "requires": { + "tslib": "2.4.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } } }, + "@chakra-ui/react-children-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-children-utils/-/react-children-utils-2.0.6.tgz", + "integrity": "sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==", + "requires": {} + }, + "@chakra-ui/react-context": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.1.0.tgz", + "integrity": "sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==", + "requires": {} + }, "@chakra-ui/react-env": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/react-env/-/react-env-1.1.6.tgz", - "integrity": "sha512-L90LNvCfe04FTkN9OPok/o2e60zLJNBH8Im/5dUHvqy7dXLXok8ZDad5vEL46XmGbhe7O8fbxhG6FmAYdcCHrQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-env/-/react-env-3.1.0.tgz", + "integrity": "sha512-Vr96GV2LNBth3+IKzr/rq1IcnkXv+MLmwjQH6C8BRtn3sNskgDFD5vLkVXcEhagzZMCh8FR3V/bzZPojBOyNhw==", + "requires": { + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" + } + }, + "@chakra-ui/react-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-types/-/react-types-2.0.7.tgz", + "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==", + "requires": {} + }, + "@chakra-ui/react-use-animation-state": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.1.0.tgz", + "integrity": "sha512-CFZkQU3gmDBwhqy0vC1ryf90BVHxVN8cTLpSyCpdmExUEtSEInSCGMydj2fvn7QXsz/za8JNdO2xxgJwxpLMtg==", + "requires": { + "@chakra-ui/dom-utils": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0" + } + }, + "@chakra-ui/react-use-callback-ref": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.1.0.tgz", + "integrity": "sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ==", + "requires": {} + }, + "@chakra-ui/react-use-controllable-state": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.1.0.tgz", + "integrity": "sha512-QR/8fKNokxZUs4PfxjXuwl0fj/d71WPrmLJvEpCTkHjnzu7LnYvzoe2wB867IdooQJL0G1zBxl0Dq+6W1P3jpg==", + "requires": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + } + }, + "@chakra-ui/react-use-disclosure": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.1.0.tgz", + "integrity": "sha512-Ax4pmxA9LBGMyEZJhhUZobg9C0t3qFE4jVF1tGBsrLDcdBeLR9fwOogIPY9Hf0/wqSlAryAimICbr5hkpa5GSw==", + "requires": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + } + }, + "@chakra-ui/react-use-event-listener": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.1.0.tgz", + "integrity": "sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q==", + "requires": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + } + }, + "@chakra-ui/react-use-focus-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.1.0.tgz", + "integrity": "sha512-xzVboNy7J64xveLcxTIJ3jv+lUJKDwRM7Szwn9tNzUIPD94O3qwjV7DDCUzN2490nSYDF4OBMt/wuDBtaR3kUQ==", "requires": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/dom-utils": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0" } }, + "@chakra-ui/react-use-focus-on-pointer-down": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.1.0.tgz", + "integrity": "sha512-2jzrUZ+aiCG/cfanrolsnSMDykCAbv9EK/4iUyZno6BYb3vziucmvgKuoXbMPAzWNtwUwtuMhkby8rc61Ue+Lg==", + "requires": { + "@chakra-ui/react-use-event-listener": "2.1.0" + } + }, + "@chakra-ui/react-use-interval": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-interval/-/react-use-interval-2.1.0.tgz", + "integrity": "sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw==", + "requires": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + } + }, + "@chakra-ui/react-use-latest-ref": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.1.0.tgz", + "integrity": "sha512-m0kxuIYqoYB0va9Z2aW4xP/5b7BzlDeWwyXCH6QpT2PpW3/281L3hLCm1G0eOUcdVlayqrQqOeD6Mglq+5/xoQ==", + "requires": {} + }, + "@chakra-ui/react-use-merge-refs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz", + "integrity": "sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==", + "requires": {} + }, + "@chakra-ui/react-use-outside-click": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.2.0.tgz", + "integrity": "sha512-PNX+s/JEaMneijbgAM4iFL+f3m1ga9+6QK0E5Yh4s8KZJQ/bLwZzdhMz8J/+mL+XEXQ5J0N8ivZN28B82N1kNw==", + "requires": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + } + }, + "@chakra-ui/react-use-pan-event": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.1.0.tgz", + "integrity": "sha512-xmL2qOHiXqfcj0q7ZK5s9UjTh4Gz0/gL9jcWPA6GVf+A0Od5imEDa/Vz+533yQKWiNSm1QGrIj0eJAokc7O4fg==", + "requires": { + "@chakra-ui/event-utils": "2.0.8", + "@chakra-ui/react-use-latest-ref": "2.1.0", + "framesync": "6.1.2" + }, + "dependencies": { + "framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "requires": { + "tslib": "2.4.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@chakra-ui/react-use-previous": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-previous/-/react-use-previous-2.1.0.tgz", + "integrity": "sha512-pjxGwue1hX8AFcmjZ2XfrQtIJgqbTF3Qs1Dy3d1krC77dEsiCUbQ9GzOBfDc8pfd60DrB5N2tg5JyHbypqh0Sg==", + "requires": {} + }, + "@chakra-ui/react-use-safe-layout-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz", + "integrity": "sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==", + "requires": {} + }, + "@chakra-ui/react-use-size": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-size/-/react-use-size-2.1.0.tgz", + "integrity": "sha512-tbLqrQhbnqOjzTaMlYytp7wY8BW1JpL78iG7Ru1DlV4EWGiAmXFGvtnEt9HftU0NJ0aJyjgymkxfVGI55/1Z4A==", + "requires": { + "@zag-js/element-size": "0.10.5" + } + }, + "@chakra-ui/react-use-timeout": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-timeout/-/react-use-timeout-2.1.0.tgz", + "integrity": "sha512-cFN0sobKMM9hXUhyCofx3/Mjlzah6ADaEl/AXl5Y+GawB5rgedgAcu2ErAgarEkwvsKdP6c68CKjQ9dmTQlJxQ==", + "requires": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + } + }, + "@chakra-ui/react-use-update-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.1.0.tgz", + "integrity": "sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA==", + "requires": {} + }, "@chakra-ui/react-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-1.2.3.tgz", - "integrity": "sha512-r8pUwCVVB7UPhb0AiRa9ZzSp4xkMz64yIeJ4O4aGy4WMw7TRH4j4QkbkE1YC9tQitrXrliOlvx4WWJR4VyiGpw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz", + "integrity": "sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==", "requires": { - "@chakra-ui/utils": "^1.10.4" + "@chakra-ui/utils": "2.0.15" + }, + "dependencies": { + "@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "requires": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "requires": { + "@types/lodash": "*" + } + }, + "framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "requires": { + "tslib": "2.4.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } } }, "@chakra-ui/select": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/select/-/select-1.2.11.tgz", - "integrity": "sha512-6Tis1+ZrRjQeWhQfziQn3ZdPphV5ccafpZOhiPdTcM2J1XcXOlII+9rHxvaW+jx7zQ5ly5o8kd7iXzalDgl5wA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/select/-/select-2.1.2.tgz", + "integrity": "sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==", "requires": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/shared-utils": "2.0.5" } }, + "@chakra-ui/shared-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz", + "integrity": "sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q==" + }, "@chakra-ui/skeleton": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@chakra-ui/skeleton/-/skeleton-1.2.14.tgz", - "integrity": "sha512-R0v4DfQ2yjXCJf9SzhTmDb2PLx5//LxsRbjjgRa8qJCR4MZaGswPrekp4dP8YjY8aEYzuZbvHU12T3vqZBk2GA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/skeleton/-/skeleton-2.1.0.tgz", + "integrity": "sha512-JNRuMPpdZGd6zFVKjVQ0iusu3tXAdI29n4ZENYwAJEMf/fN0l12sVeirOxkJ7oEL0yOx2AgEYFSKdbcAgfUsAQ==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/media-query": "2.0.4", - "@chakra-ui/system": "1.12.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/media-query": "3.3.0", + "@chakra-ui/react-use-previous": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, + "@chakra-ui/skip-nav": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/skip-nav/-/skip-nav-2.1.0.tgz", + "integrity": "sha512-Hk+FG+vadBSH0/7hwp9LJnLjkO0RPGnx7gBJWI4/SpoJf3e4tZlWYtwGj0toYY4aGKl93jVghuwGbDBEMoHDug==", + "requires": {} + }, "@chakra-ui/slider": { - "version": "1.5.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-1.5.11.tgz", - "integrity": "sha512-THkGU2BsA6XMosXcEVQkWVRftqUIAKCb+y4iEpR3C2ztqL7Fl/CbIGwyr5majhPhKc275rb8dfxwp8R0L0ZIiQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-2.1.0.tgz", + "integrity": "sha512-lUOBcLMCnFZiA/s2NONXhELJh6sY5WtbRykPtclGfynqqOo47lwWJx+VP7xaeuhDOPcWSSecWc9Y1BfPOCz9cQ==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/number-utils": "2.0.7", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-latest-ref": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-pan-event": "2.1.0", + "@chakra-ui/react-use-size": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0" } }, "@chakra-ui/spinner": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/spinner/-/spinner-1.2.6.tgz", - "integrity": "sha512-GoUCccN120fGRVgUtfuwcEjeoaxffB+XsgpxX7jhWloXf8b6lkqm68bsxX4Ybb2vGN1fANI98/45JmrnddZO/A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/spinner/-/spinner-2.1.0.tgz", + "integrity": "sha512-hczbnoXt+MMv/d3gE+hjQhmkzLiKuoTo42YhUG7Bs9OSv2lg1fZHW1fGNRFP3wTi6OIbD044U1P9HK+AOgFH3g==", "requires": { - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/stat": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/stat/-/stat-1.2.7.tgz", - "integrity": "sha512-m76jumFW1N+mCG4ytrUz9Mh09nZtS4OQcADEvOslfdI5StwwuzasTA1tueaelPzdhBioMwFUWL05Fr1fXbPJ/Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/stat/-/stat-2.1.1.tgz", + "integrity": "sha512-LDn0d/LXQNbAn2KaR3F1zivsZCewY4Jsy1qShmfBMKwn6rI8yVlbvu6SiA3OpHS0FhxbsZxQI6HefEoIgtqY6Q==", "requires": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + } + }, + "@chakra-ui/stepper": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/stepper/-/stepper-2.3.1.tgz", + "integrity": "sha512-ky77lZbW60zYkSXhYz7kbItUpAQfEdycT0Q4bkHLxfqbuiGMf8OmgZOQkOB9uM4v0zPwy2HXhe0vq4Dd0xa55Q==", + "requires": { + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/styled-system": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-1.19.0.tgz", - "integrity": "sha512-z+bMfWs6jQGkpgarge1kmk78DuDhJIXRUMyRqZ3+CiIkze88bIIsww6mV2i8tEfUfTAvALeMnlYZ1DYsHsTTJw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz", + "integrity": "sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg==", "requires": { - "@chakra-ui/utils": "1.10.4", - "csstype": "3.0.9" - }, - "dependencies": { - "csstype": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", - "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" - } + "@chakra-ui/shared-utils": "2.0.5", + "csstype": "^3.1.2", + "lodash.mergewith": "4.6.2" } }, "@chakra-ui/switch": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/switch/-/switch-1.3.10.tgz", - "integrity": "sha512-V6qDLY6oECCbPyu7alWWOAhSBI4+SAuT6XW/zEQbelkwuUOiGO1ax67rTXOmZ59A2AaV1gqQFxDh8AcbvwO5XQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/switch/-/switch-2.1.2.tgz", + "integrity": "sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==", "requires": { - "@chakra-ui/checkbox": "1.7.1", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/checkbox": "2.3.2", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/system": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-1.12.1.tgz", - "integrity": "sha512-Rp09/rMuPA3hF38OJxeQciGO9N0Ie1GxwHRAw1AFA/TY3fVyK9pNI5oN+J/1cAxq7v9yKdIr1YfnruJTI9xfEg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-2.6.2.tgz", + "integrity": "sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==", "requires": { - "@chakra-ui/color-mode": "1.4.8", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/styled-system": "1.19.0", - "@chakra-ui/utils": "1.10.4", - "react-fast-compare": "3.2.0" + "@chakra-ui/color-mode": "2.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-utils": "2.0.12", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme-utils": "2.0.21", + "@chakra-ui/utils": "2.0.15", + "react-fast-compare": "3.2.2" }, "dependencies": { + "@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "requires": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "requires": { + "@types/lodash": "*" + } + }, + "framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "requires": { + "tslib": "2.4.0" + } + }, "react-fast-compare": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", - "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" } } }, "@chakra-ui/table": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/table/-/table-1.3.6.tgz", - "integrity": "sha512-7agZAgAeDFKviqStvixqnLAH54+setzhx67EztioZTr5Xu+6hQ4rotfJbu8L4i587pcbNg98kCEXEkidjw0XRQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/table/-/table-2.1.0.tgz", + "integrity": "sha512-o5OrjoHCh5uCLdiUb0Oc0vq9rIAeHSIRScc2ExTC9Qg/uVZl2ygLrjToCaKfaaKl1oQexIeAcZDKvPG8tVkHyQ==", "requires": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/tabs": { - "version": "1.6.10", - "resolved": "https://registry.npmjs.org/@chakra-ui/tabs/-/tabs-1.6.10.tgz", - "integrity": "sha512-ClOOHT3Wnf3l9X4F2S6ysPsHMDgKSTgkXpB9Qe0odwpT49ZXNjSAYYaXzO16l+Eq/m2u1HzLkXVsL42HIeOiNQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/tabs/-/tabs-3.0.0.tgz", + "integrity": "sha512-6Mlclp8L9lqXmsGWF5q5gmemZXOiOYuh0SGT/7PgJVNPz3LXREXlXg2an4MBUD8W5oTkduCX+3KTMCwRrVrDYw==", "requires": { - "@chakra-ui/clickable": "1.2.6", - "@chakra-ui/descendant": "2.1.3", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/clickable": "2.1.0", + "@chakra-ui/descendant": "3.1.0", + "@chakra-ui/lazy-utils": "2.0.5", + "@chakra-ui/react-children-utils": "2.0.6", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-controllable-state": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/tag": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@chakra-ui/tag/-/tag-1.2.7.tgz", - "integrity": "sha512-RKrKOol4i/CnpFfo3T9LMm1abaqM+5Bs0soQLbo1iJBbBACY09sWXrQYvveQ2GYzU/OrAUloHqqmKjyVGOlNtg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/tag/-/tag-3.1.1.tgz", + "integrity": "sha512-Bdel79Dv86Hnge2PKOU+t8H28nm/7Y3cKd4Kfk9k3lOpUh4+nkSGe58dhRzht59lEqa4N9waCgQiBdkydjvBXQ==", "requires": { - "@chakra-ui/icon": "2.0.5", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0" } }, "@chakra-ui/textarea": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@chakra-ui/textarea/-/textarea-1.2.11.tgz", - "integrity": "sha512-RDWbMyC87/AFRX98EnVum5eig/7hhcvS1BrqW5lvmTgrpr7KVr80Dfa8hUj58Iq37Z7AqZijDPkBn/zg7bPdIg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/textarea/-/textarea-2.1.2.tgz", + "integrity": "sha512-ip7tvklVCZUb2fOHDb23qPy/Fr2mzDOGdkrpbNi50hDCiV4hFX02jdQJdi3ydHZUyVgZVBKPOJ+lT9i7sKA2wA==", "requires": { - "@chakra-ui/form-control": "1.6.0", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/theme": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-1.14.1.tgz", - "integrity": "sha512-VeNZi+zD3yDwzvZm234Cy3vnalCzQ+dhAgpHdIYzGO1CYO8DPa+ROcQ70rUueL7dSvUz15KOiGTw6DAl7LXlGA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.3.1.tgz", + "integrity": "sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ==", "requires": { - "@chakra-ui/anatomy": "1.3.0", - "@chakra-ui/theme-tools": "1.3.6", - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/theme-tools": "2.1.2" } }, "@chakra-ui/theme-tools": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-1.3.6.tgz", - "integrity": "sha512-Wxz3XSJhPCU6OwCHEyH44EegEDQHwvlsx+KDkUDGevOjUU88YuNqOVkKtgTpgMLNQcsrYZ93oPWZUJqqCVNRew==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz", + "integrity": "sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA==", "requires": { - "@chakra-ui/utils": "1.10.4", - "@ctrl/tinycolor": "^3.4.0" + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "color2k": "^2.0.2" } }, - "@chakra-ui/toast": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@chakra-ui/toast/-/toast-1.5.9.tgz", - "integrity": "sha512-rns04bGdMcG7Ijg45L+PfuEW4rCd0Ycraix4EJQhcl9RXI18G9sphmlp9feidhZAkI6Ukafq1YvyvkBfkKnIzQ==", - "requires": { - "@chakra-ui/alert": "1.3.7", - "@chakra-ui/close-button": "1.2.7", - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/theme": "1.14.1", - "@chakra-ui/transition": "1.4.8", - "@chakra-ui/utils": "1.10.4", - "@reach/alert": "0.13.2" - }, - "dependencies": { - "@reach/alert": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@reach/alert/-/alert-0.13.2.tgz", - "integrity": "sha512-LDz83AXCrClyq/MWe+0vaZfHp1Ytqn+kgL5VxG7rirUvmluWaj/snxzfNPWn0Ma4K2YENmXXRC/iHt5X95SqIg==", - "requires": { - "@reach/utils": "0.13.2", - "@reach/visually-hidden": "0.13.2", - "prop-types": "^15.7.2", - "tslib": "^2.1.0" - }, - "dependencies": { - "@reach/utils": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.13.2.tgz", - "integrity": "sha512-3ir6cN60zvUrwjOJu7C6jec/samqAeyAB12ZADK+qjnmQPdzSYldrFWwDVV5H0WkhbYXR3uh+eImu13hCetNPQ==", - "requires": { - "@types/warning": "^3.0.0", - "tslib": "^2.1.0", - "warning": "^4.0.3" - } - }, - "@reach/visually-hidden": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@reach/visually-hidden/-/visually-hidden-0.13.2.tgz", - "integrity": "sha512-sPZwNS0/duOuG0mYwE5DmgEAzW9VhgU3aIt1+mrfT/xiT9Cdncqke+kRBQgU708q/Ttm9tWsoHni03nn/SuPTQ==", - "requires": { - "prop-types": "^15.7.2", - "tslib": "^2.1.0" - } - } - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - } + "@chakra-ui/theme-utils": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz", + "integrity": "sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw==", + "requires": { + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme": "3.3.1", + "lodash.mergewith": "4.6.2" } }, - "@chakra-ui/tooltip": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@chakra-ui/tooltip/-/tooltip-1.5.1.tgz", - "integrity": "sha512-EUAlDdlCBt63VpEVtj/RkFjHQVN/xA9gEAumngQdi1Sp+OXPYCBM9GwSY0NwrM1RfKBnhPSH9wz7FwredJWeaw==", + "@chakra-ui/toast": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/toast/-/toast-7.0.2.tgz", + "integrity": "sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==", "requires": { - "@chakra-ui/hooks": "1.9.1", - "@chakra-ui/popper": "2.4.3", - "@chakra-ui/portal": "1.3.10", - "@chakra-ui/react-utils": "1.2.3", - "@chakra-ui/utils": "1.10.4", - "@chakra-ui/visually-hidden": "1.1.6" + "@chakra-ui/alert": "2.2.2", + "@chakra-ui/close-button": "2.1.1", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-use-timeout": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme": "3.3.1" } }, - "@chakra-ui/transition": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@chakra-ui/transition/-/transition-1.4.8.tgz", - "integrity": "sha512-5uc8LEuCH7+0h++wqAav/EktTHOjbLDSTXQlU9fzPIlNNgyf2eXrHVN2AGMGKiMR9Z4gS7umQjZ54r0w/mZ/Fw==", + "@chakra-ui/tooltip": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/tooltip/-/tooltip-2.3.1.tgz", + "integrity": "sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==", "requires": { - "@chakra-ui/utils": "1.10.4" + "@chakra-ui/dom-utils": "2.1.0", + "@chakra-ui/popper": "3.1.0", + "@chakra-ui/portal": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-disclosure": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" } }, - "@chakra-ui/utils": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-1.10.4.tgz", - "integrity": "sha512-AM91VQQxw8F4F1WDA28mqKY6NFIOuzc2Ekkna88imy2OiqqmYH0xkq8J16L2qj4cLiLozpYqba3C79pWioy6FA==", + "@chakra-ui/transition": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/transition/-/transition-2.1.0.tgz", + "integrity": "sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ==", "requires": { - "@types/lodash.mergewith": "4.6.6", - "css-box-model": "1.2.1", - "framesync": "5.3.0", - "lodash.mergewith": "4.6.2" + "@chakra-ui/shared-utils": "2.0.5" } }, "@chakra-ui/visually-hidden": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@chakra-ui/visually-hidden/-/visually-hidden-1.1.6.tgz", - "integrity": "sha512-Xzy5bA0UA+IyMgwJizQYSEdgz8cC/tHdmFB3CniXzmpKTSK8mJddeEBl+cGbXHBzxEUhH7xF1eaS41O+0ezWEQ==", - "requires": { - "@chakra-ui/utils": "1.10.4" - } + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/visually-hidden/-/visually-hidden-2.2.0.tgz", + "integrity": "sha512-KmKDg01SrQ7VbTD3+cPWf/UfpF5MSwm3v7MWi0n5t8HnnadT13MF0MJCDSXbBWnzLv1ZKJ6zlyAOeARWX+DpjQ==", + "requires": {} }, "@cnakazawa/watch": { "version": "1.0.4", @@ -22504,11 +25922,6 @@ "minimist": "^1.2.0" } }, - "@ctrl/tinycolor": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz", - "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==" - }, "@discoveryjs/json-ext": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz", @@ -22564,18 +25977,6 @@ } } }, - "@emotion/css": { - "version": "11.11.2", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.11.2.tgz", - "integrity": "sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew==", - "requires": { - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1" - } - }, "@emotion/hash": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", @@ -22676,25 +26077,180 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, - "@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", - "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", + "@esbuild/aix-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", "dev": true, - "requires": { - "lodash.get": "^4", - "make-error": "^1", - "ts-node": "^9", - "tslib": "^2" - }, - "dependencies": { - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - } - } + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "dev": true, + "optional": true }, "@eslint-community/eslint-utils": { "version": "4.4.0", @@ -22768,6 +26324,11 @@ } } }, + "@gilbarbara/deep-equal": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz", + "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw==" + }, "@graphql-tools/merge": { "version": "8.3.3", "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.3.tgz", @@ -22931,6 +26492,71 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -23297,6 +26923,15 @@ } } }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "devOptional": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, "@jest/source-map": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", @@ -23434,15 +27069,6 @@ "chalk": "^4.0.0" }, "dependencies": { - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -23513,26 +27139,46 @@ "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "devOptional": true + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" }, "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "devOptional": true }, + "@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "devOptional": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", - "devOptional": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "requires": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -23544,147 +27190,180 @@ "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" }, "@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, "@lingui/babel-plugin-extract-messages": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.14.0.tgz", - "integrity": "sha512-4lcDgVdjYiObuFdDwnAG3jJxS+d3YLq4i7qywlHgjIqteKUH01S3paJRXhZaPvLGl56HarSq0kt8Pymxw8lOrA==", - "dev": true, + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-5.9.3.tgz", + "integrity": "sha512-zm6QHDILmhj8olgLL2zHQn18yFA5mf4hX7QzCr1OOI/e815I0IkecCYue1Ych+y+B+V0eLriiW8AcfpDRCQFFw==", + "dev": true + }, + "@lingui/babel-plugin-lingui-macro": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-lingui-macro/-/babel-plugin-lingui-macro-5.9.3.tgz", + "integrity": "sha512-fLMhBarRsuqBGOq2YuEoqOjEAV2VNezz/+f/Dn0vLFHF/kAjnFwTHb8pL8DRSIMsWG16mPrGnLpdROZBmJlFtA==", + "devOptional": true, "requires": { - "@babel/generator": "^7.11.6", - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.14.0", - "mkdirp": "^1.0.4" + "@babel/core": "^7.20.12", + "@babel/runtime": "^7.20.13", + "@babel/types": "^7.20.7", + "@lingui/conf": "5.9.3", + "@lingui/core": "5.9.3", + "@lingui/message-utils": "5.9.3" } }, "@lingui/cli": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.14.0.tgz", - "integrity": "sha512-QZURsIf7A97tf28b/ffpeL0DekA6tBmcwnj4FBui1SbQqJw1d4IPg2bUM5VRn3/25vhqpi9Uhx5m9x7Vv8QfCQ==", - "dev": true, - "requires": { - "@babel/generator": "^7.11.6", - "@babel/parser": "^7.11.5", - "@babel/plugin-syntax-jsx": "^7.10.4", - "@babel/runtime": "^7.11.2", - "@babel/types": "^7.11.5", - "@lingui/babel-plugin-extract-messages": "^3.14.0", - "@lingui/conf": "^3.14.0", - "babel-plugin-macros": "^3.0.1", - "bcp-47": "^1.0.7", - "chalk": "^4.1.0", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-5.9.3.tgz", + "integrity": "sha512-KEE0J4eGlfpiLZ+l019qjraWfqfh5mUmQSJeTFw5PulO4v50zvxw5tDX8stpBzJ3QtgUQZlrMUh0OTGdURaAMg==", + "dev": true, + "requires": { + "@babel/core": "^7.21.0", + "@babel/generator": "^7.21.1", + "@babel/parser": "^7.22.0", + "@babel/runtime": "^7.21.0", + "@babel/types": "^7.21.2", + "@lingui/babel-plugin-extract-messages": "5.9.3", + "@lingui/babel-plugin-lingui-macro": "5.9.3", + "@lingui/conf": "5.9.3", + "@lingui/core": "5.9.3", + "@lingui/format-po": "5.9.3", + "@lingui/message-utils": "5.9.3", "chokidar": "3.5.1", - "cli-table": "0.3.6", - "commander": "^6.1.0", - "date-fns": "^2.16.1", - "fs-extra": "^9.0.1", - "fuzzaldrin": "^2.1.0", - "glob": "^7.1.4", - "inquirer": "^7.3.3", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3", - "micromatch": "4.0.2", - "mkdirp": "^1.0.4", - "node-gettext": "^3.0.0", + "cli-table": "^0.3.11", + "commander": "^10.0.0", + "convert-source-map": "^2.0.0", + "date-fns": "^3.6.0", + "esbuild": "^0.25.1", + "glob": "^11.0.0", + "micromatch": "^4.0.7", + "ms": "^2.1.3", "normalize-path": "^3.0.0", "ora": "^5.1.0", - "papaparse": "^5.3.0", - "pkg-up": "^3.1.0", - "plurals-cldr": "^1.0.4", - "pofile": "^1.1.0", - "pseudolocale": "^1.1.0", - "ramda": "^0.27.1" + "picocolors": "^1.1.1", + "pofile": "^1.1.4", + "pseudolocale": "^2.0.0", + "source-map": "^0.7.6", + "threads": "^1.7.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, + "brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "balanced-match": "^4.0.2" } }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "requires": { - "color-name": "~1.1.4" + "brace-expansion": "^5.0.5" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==", + "source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "@lingui/conf": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.14.0.tgz", - "integrity": "sha512-5GMAbIRad9FavqYsfZCRAwjcOLzE7tONDJe9lSYE5SSJbbG01RI5kR5P0B84DUhTI6cGXau+1dAcP9K+JbEx+g==", - "dev": true, + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-5.9.3.tgz", + "integrity": "sha512-hVEoYHmO2A3XmFX4A5RuBgcoVBoM7Xgoqejeq25XELvesJj2s2T15F47TA5n3/S7iTqngd6n/8KxBli9TYwgqQ==", + "devOptional": true, "requires": { - "@babel/runtime": "^7.11.2", - "@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "jest-validate": "^26.5.2", - "lodash.get": "^4.4.2" + "@babel/runtime": "^7.20.13", + "cosmiconfig": "^8.0.0", + "jest-validate": "^29.4.3", + "jiti": "^2.5.1", + "picocolors": "^1.1.1" }, "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "devOptional": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "devOptional": true, + "requires": { + "@types/yargs-parser": "*" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "requires": { "color-convert": "^2.0.1" } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "devOptional": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -23694,7 +27373,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "requires": { "color-name": "~1.1.4" } @@ -23703,19 +27382,85 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devOptional": true + }, + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "devOptional": true, + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + } }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "devOptional": true + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "devOptional": true + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "devOptional": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + } + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "devOptional": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "devOptional": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "devOptional": true + } + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "devOptional": true }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, "requires": { "has-flag": "^4.0.0" } @@ -23723,53 +27468,71 @@ } }, "@lingui/core": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.14.0.tgz", - "integrity": "sha512-ertREq9oi9B/umxpd/pInm9uFO8FLK2/0FXfDmMqvH5ydswWn/c9nY5YO4W1h4/8LWO45mewypOIyjoue4De1w==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.9.3.tgz", + "integrity": "sha512-3b8LnDjx8POdQ6q6UKBe2DHynyQFCO66vm8/UPQnvlQowUk4Xdu5bK6oet11D9/vrSznrDDS+Qb5JVcNBUImgg==", "requires": { - "@babel/runtime": "^7.11.2", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3" - }, - "dependencies": { - "make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" - } + "@babel/runtime": "^7.20.13", + "@lingui/message-utils": "5.9.3" + } + }, + "@lingui/format-po": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-5.9.3.tgz", + "integrity": "sha512-+LMnhWl7EmXrdOv10gopE1g8w8vtPY5Fxk72OORrGQFVMGBIioz4BEnKrNdV1ek2M+GxoMZtnUs17KrJN5Jv9A==", + "dev": true, + "requires": { + "@lingui/conf": "5.9.3", + "@lingui/message-utils": "5.9.3", + "date-fns": "^3.6.0", + "pofile": "^1.1.4" } }, "@lingui/loader": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-3.14.0.tgz", - "integrity": "sha512-FH14MexRLM8tBgSL1oK5dbaUBbY0P85CSLTNwFqAtFFj8K8wWxfdq4+rIkp/kSD+eggKNDYV4G89N9/3bsm3gQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-5.9.3.tgz", + "integrity": "sha512-V+m8vfZ1doPSc26fPZa1zVso75nl70mgdz51OHGAvFfafyDqYasFHmBf5xihS58vy6LXuep3K19Ymf6TG6i5TQ==", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/cli": "^3.14.0", - "@lingui/conf": "^3.14.0", - "loader-utils": "^2.0.0", - "ramda": "^0.27.1" + "@babel/runtime": "^7.20.13", + "@lingui/cli": "5.9.3", + "@lingui/conf": "5.9.3" } }, "@lingui/macro": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.14.0.tgz", - "integrity": "sha512-NxTRrhrZ/cUO9PX/4vWys90Ku58+ExxHuE30IuDnnDldWhWlOdycmjDt9tB+yIiUdFym/veSxBs+h114FzG5mA==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-5.9.3.tgz", + "integrity": "sha512-xWqJ+hpp5T+xmE++VYlcfMxrF48LpY5Ak9N/luibY9d0AqvyYZiiv7Xaq7E2eK69v9CJWx+6eXA6uPhC8gHY+Q==", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.14.0", - "ramda": "^0.27.1" + "@lingui/core": "5.9.3", + "@lingui/react": "5.9.3" + } + }, + "@lingui/message-utils": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.9.3.tgz", + "integrity": "sha512-oAK7HA7lcQrzaEaM6G1T5RwwxJxaSKfG/IFIxpZIl49TSFQv+s9YPNgHnVi+d4DmterpXNxy9ZZ+NtckJx6u7g==", + "requires": { + "@messageformat/parser": "^5.0.0", + "js-sha256": "^0.10.1" } }, "@lingui/react": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/react/-/react-3.14.0.tgz", - "integrity": "sha512-ow9Mtru7f0T2S9AwnPWRejppcucCW0LmoDR3P4wqHjL+eH5f8a6nxd2doxGieC91/2i4qqW88y4K/zXJxwRSQw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@lingui/react/-/react-5.9.3.tgz", + "integrity": "sha512-aje78l3zGGZ3C75fiGhDVKyVALHfiKlYFjcOlZpgXY/JAVfFuZX+6wUGG9x1A8k7BfxrDy/ofHIBahPvNAqoKw==", "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/core": "^3.14.0" + "@babel/runtime": "^7.20.13", + "@lingui/core": "5.9.3" + } + }, + "@messageformat/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==", + "requires": { + "moo": "^0.5.1" } }, "@nicolo-ribaudo/chokidar-2": { @@ -23779,6 +27542,12 @@ "dev": true, "optional": true }, + "@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -23805,10 +27574,269 @@ "fastq": "^1.6.0" } }, + "@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "requires": { + "@noble/hashes": "^1.1.5" + } + }, + "@peculiar/asn1-cms": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.7.0.tgz", + "integrity": "sha512-hew63shtzzvBcSHbhm+cyAmKe6AIfinT9hzEqSPjDC6opTTMKmTkQ0gHuN2KsWlvqiKw1S/fS94fhag/FJkioQ==", + "dev": true, + "requires": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "@peculiar/asn1-x509-attr": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-csr": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.7.0.tgz", + "integrity": "sha512-VVsAyGqErT9D1SY4aEqozThXMVI+ssVRiv2DDeYuvpBKLIgZ3hYs3Ay3u/VSoKq6ESFi9cf6rf3IOOzfwh7oMA==", + "dev": true, + "requires": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-ecc": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.7.0.tgz", + "integrity": "sha512-n7KEs/Q/wrB415cxy4fHOBhegp4NdJ15fkJPwcB/3/8iNBQC2L/N7SChJPKDJPZGYH0jD4Tg4/0vnHmwghnbKw==", + "dev": true, + "requires": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-pfx": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.7.0.tgz", + "integrity": "sha512-V/nrlQVmhg7lYAsM7E13UDL5erAwFv6kCIVFqNaMIHSVi7dngcT839JkRTkQBqznMG98l2XjxYk74ZztAohZzA==", + "dev": true, + "requires": { + "@peculiar/asn1-cms": "^2.7.0", + "@peculiar/asn1-pkcs8": "^2.7.0", + "@peculiar/asn1-rsa": "^2.7.0", + "@peculiar/asn1-schema": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-pkcs8": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.7.0.tgz", + "integrity": "sha512-9GTl1nE8Mx1kTZ+7QyYatDyKsm34QcWRBFkY1iPvWC3X4Dona5s/tlLiQsx5WzVdZqiMBZNYT0buyw4/vbhnjw==", + "dev": true, + "requires": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-pkcs9": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.7.0.tgz", + "integrity": "sha512-Bh7m+OuIaSEllPQcSd9OSp93F4ROWH7sbITWV8MI+8dwsjE5111/87VxiWVvYFKyww3vp39geLv9ENqhwWHcew==", + "dev": true, + "requires": { + "@peculiar/asn1-cms": "^2.7.0", + "@peculiar/asn1-pfx": "^2.7.0", + "@peculiar/asn1-pkcs8": "^2.7.0", + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "@peculiar/asn1-x509-attr": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-rsa": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.7.0.tgz", + "integrity": "sha512-/qvENQrXyTZURjMqSeofHul0JJt2sNSzSwk36pl2olkHbaioMQgrASDZAlHXl0xUlnVbHj0uGgOrBMTb5x2aJQ==", + "dev": true, + "requires": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-schema": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.7.0.tgz", + "integrity": "sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg==", + "dev": true, + "requires": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-x509": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.7.0.tgz", + "integrity": "sha512-mUn9RRrkGDnG4ALfunDmzyRW5dg+sWCj/pfnCCqEHYbkGxEpvUt6iVJv8Yw1cyp6SWZ26ZE5oSmI5SqEaen15g==", + "dev": true, + "requires": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/asn1-x509-attr": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.7.0.tgz", + "integrity": "sha512-NS8e7SOgXipkzUPLF/sce7ukpMpWjhxYsH0n6Y+bHYo4TTxOb95Zv7hqwSuL212mj5YxovjdOKQOgH1As3E94w==", + "dev": true, + "requires": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/asn1-x509": "^2.7.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "requires": { + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@peculiar/x509": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", + "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", + "dev": true, + "requires": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-csr": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.0", + "@peculiar/asn1-pkcs9": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, "@popperjs/core": { - "version": "2.11.5", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", - "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==" + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -23869,11 +27897,22 @@ "dev": true }, "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "dev": true }, + "@remix-run/router": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz", + "integrity": "sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==" + }, + "@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "devOptional": true + }, "@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -24164,9 +28203,9 @@ } }, "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "requires": { "@types/node": "*" @@ -24182,9 +28221,9 @@ } }, "@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "requires": { "@types/express-serve-static-core": "*", @@ -24197,17 +28236,32 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, + "@types/d3-array": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz", + "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==" + }, "@types/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.5.tgz", + "integrity": "sha512-5sNP3DmtSnSozxcjqmzQKsDOuVJXZkceo1KJScDc1982kk/TS9mTPc6lpli1gTu1MIBF1YWutpHpjucNWcIj5g==" + }, + "@types/d3-delaunay": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", + "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==" }, - "@types/d3-interpolate": { + "@types/d3-format": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", + "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==" + }, + "@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", "requires": { - "@types/d3-color": "*" + "@types/geojson": "*" } }, "@types/d3-path": { @@ -24215,13 +28269,10 @@ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz", "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==" }, - "@types/d3-scale": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", - "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", - "requires": { - "@types/d3-time": "*" - } + "@types/d3-random": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-2.2.3.tgz", + "integrity": "sha512-Ghs4R3CcgJ3o6svszRzIH4b8PPYex/COo+rhhZjDAs+bVducXwjmVSi27WcDOaLLCBV2t3tfVH9bYXAL76IvQA==" }, "@types/d3-shape": { "version": "1.3.8", @@ -24232,14 +28283,19 @@ } }, "@types/d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.4.tgz", + "integrity": "sha512-BTfLsxTeo7yFxI/haOOf1ZwJ6xKgQLT9dCp+EcmQv87Gox6X+oKl4mLKfO6fnWm3P22+A6DknMNEZany8ql2Rw==" + }, + "@types/d3-time-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz", + "integrity": "sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==" }, "@types/eslint": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "requires": { "@types/estree": "*", @@ -24247,9 +28303,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "requires": { "@types/eslint": "*", @@ -24257,9 +28313,9 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "@types/express": { @@ -24285,6 +28341,11 @@ "@types/range-parser": "*" } }, + "@types/geojson": { + "version": "7946.0.13", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", + "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==" + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -24310,6 +28371,12 @@ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", "dev": true }, + "@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true + }, "@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -24323,17 +28390,26 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true + "devOptional": true }, "@types/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, + "devOptional": true, "requires": { "@types/istanbul-lib-coverage": "*" } }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "devOptional": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, "@types/jest": { "version": "26.0.15", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz", @@ -24345,9 +28421,9 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/json5": { @@ -24361,14 +28437,6 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==" }, - "@types/lodash.mergewith": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz", - "integrity": "sha512-RY/8IaVENjG19rxTZu9Nukqh0W2UrYgmBj5sdns4hWRZaV8PqR7wIKHFKzvOTjo4zVRV7sVI+yFhAJql12Kfqg==", - "requires": { - "@types/lodash": "*" - } - }, "@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -24376,9 +28444,9 @@ "dev": true }, "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "@types/minimatch": { @@ -24391,7 +28459,7 @@ "version": "14.0.26", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.26.tgz", "integrity": "sha512-W+fpe5s91FBGE0pEa0lnqGLL4USgpLgs4nokw16SrBBco/gQxuua7KnArSEOd5iaMqbbSHV10vUDkJYJJqpXKA==", - "dev": true + "devOptional": true }, "@types/normalize-package-data": { "version": "2.4.0", @@ -24455,9 +28523,9 @@ } }, "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", "dev": true }, "@types/scheduler": { @@ -24471,29 +28539,40 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "requires": { "@types/express": "*" } }, "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "dev": true, "requires": { - "@types/mime": "^1", - "@types/node": "*" + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" } }, "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "requires": { "@types/node": "*" @@ -24543,11 +28622,6 @@ } } }, - "@types/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==" - }, "@types/webpack": { "version": "4.41.21", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.21.tgz", @@ -24589,6 +28663,15 @@ } } }, + "@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "15.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", @@ -24602,7 +28685,7 @@ "version": "15.0.0", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", - "dev": true + "devOptional": true }, "@typescript-eslint/scope-manager": { "version": "5.62.0", @@ -24741,17 +28824,69 @@ "prop-types": "^15.6.0" }, "dependencies": { - "@visx/point": { + "@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@visx/curve": { "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.17.0.tgz", - "integrity": "sha512-fUdGQBLGaSVbFTbQ6k+1nPisbqYjTjAdo9FhlwLd3W3uyXN/39Sx2z3N2579sVNBDzmCKdYNQIU0HC+/3Vqo6w==" + "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.17.0.tgz", + "integrity": "sha512-8Fw2ZalgYbpeoelLqTOmMs/wD8maSKsKS9rRIwmHZ0O0XxY8iG9oVYbD4CLWzf/uFWCY6+qofk4J1g9BWQSXJQ==", + "requires": { + "@types/d3-shape": "^1.3.1", + "d3-shape": "^1.0.6" + } + }, + "@visx/scale": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.17.0.tgz", + "integrity": "sha512-ok0RUOSp9VxZzuwo/1I9nsxZxeAdU6wsvIb+cEyMrCuDwm79wzaioSkafAGSb39cKYNrGobFlA3vUd7+JZPCaw==", + "requires": { + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-time": "^2.0.0", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-time": "^2.1.1" + } + }, + "@visx/shape": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.17.0.tgz", + "integrity": "sha512-c2uun6f9souLIyUx+WLetG2JSJ4hF3dJqs1yoFZuO5BLNcU35LTCbqvEq10hLPB7TLqkA0s3jWt/rpE4M3S0Mw==", + "requires": { + "@types/d3-path": "^1.0.8", + "@types/d3-shape": "^1.3.1", + "@types/lodash": "^4.14.172", + "@types/react": "*", + "@visx/curve": "2.17.0", + "@visx/group": "2.17.0", + "@visx/scale": "2.17.0", + "classnames": "^2.3.1", + "d3-path": "^1.0.5", + "d3-shape": "^1.2.0", + "lodash": "^4.17.21", + "prop-types": "^15.5.10" + } } } }, "@visx/bounds": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.10.0.tgz", - "integrity": "sha512-rY7WFTIjQaXA8tFL45O2qbtSRkyF4yF75HiWz06F7BVmJ9UjF2qlomB3Y1z6gk6ZiFhwQ4zxABjOVjAQPLn7nQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.17.0.tgz", + "integrity": "sha512-XsoyTAyCm+DZbrPgP3IZFZAcNqBmXFBLSep04TqnrEA3hf16GxIzcpaGe+hAVhPg5yzBdjc7tLk6s0h5F44niA==", "requires": { "@types/react": "*", "@types/react-dom": "*", @@ -24759,42 +28894,86 @@ } }, "@visx/curve": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.17.0.tgz", - "integrity": "sha512-8Fw2ZalgYbpeoelLqTOmMs/wD8maSKsKS9rRIwmHZ0O0XxY8iG9oVYbD4CLWzf/uFWCY6+qofk4J1g9BWQSXJQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-3.3.0.tgz", + "integrity": "sha512-G1l1rzGWwIs8ka3mBhO/gj8uYK6XdU/3bwRSoiZ+MockMahQFPog0bUkuVgPwwzPSJfsA/E5u53Y/DNesnHQxg==", "requires": { "@types/d3-shape": "^1.3.1", "d3-shape": "^1.0.6" } }, "@visx/event": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz", - "integrity": "sha512-WGp91g82s727g3NAnENF1ppC3ZAlvWg+Y+GG0WFg34NmmOZbvPI/PTOqTqZE3x6B8EUn8NJiMxRjxIMbi+IvRw==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.17.0.tgz", + "integrity": "sha512-fg2UWo89RgKgWWnnqI+i7EF8Ry+3CdMHTND4lo4DyJvcZZUCOwhxCHMQ4/PHW0EAUfxI51nGadcE1BcEVR5zWw==", "requires": { "@types/react": "*", - "@visx/point": "2.6.0" + "@visx/point": "2.17.0" + } + }, + "@visx/glyph": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/glyph/-/glyph-3.3.0.tgz", + "integrity": "sha512-U2r1rFLpim3afKuuAmrbxXGSDCaLwXHmjXxWN8PiIQPMxpS7eaa/V5g2TRd/+x0KCkaf3Ismk4VKMl8ZlrmxIQ==", + "requires": { + "@types/d3-shape": "^1.3.1", + "@types/react": "*", + "@visx/group": "3.3.0", + "classnames": "^2.3.1", + "d3-shape": "^1.2.0", + "prop-types": "^15.6.2" + }, + "dependencies": { + "@visx/group": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/group/-/group-3.3.0.tgz", + "integrity": "sha512-yKepDKwJqlzvnvPS0yDuW13XNrYJE4xzT6xM7J++441nu6IybWWwextyap8ey+kU651cYDb+q1Oi6aHvQwyEyw==", + "requires": { + "@types/react": "*", + "classnames": "^2.3.1", + "prop-types": "^15.6.2" + } + } + } + }, + "@visx/gradient": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/gradient/-/gradient-3.3.0.tgz", + "integrity": "sha512-t3vqukahDQsJ64/fcm85woFm2XPpSPMBz92gFvaY4J8EJY3e6rFOg382v5Dm17fgNsLRKJA0Vqo7mUtDe2pWOw==", + "requires": { + "@types/react": "*", + "prop-types": "^15.5.7" } }, "@visx/grid": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/grid/-/grid-2.17.0.tgz", - "integrity": "sha512-lRs9PPx/QLFjQmXBiX42KXMbOTrywP0Os5v6D8sN+WSB72Gn4LDhVYeHqjFetZqk+ttDNbjE/Vecq+X6JWSCkQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/grid/-/grid-3.5.0.tgz", + "integrity": "sha512-i1pdobTE223ItMiER3q4ojIaZWja3vg46TkS6FotnBZ4c0VRDHSrALQPdi0na+YEgppASWCQ2WrI/vD6mIkhSg==", "requires": { "@types/react": "*", - "@visx/curve": "2.17.0", - "@visx/group": "2.17.0", - "@visx/point": "2.17.0", - "@visx/scale": "2.17.0", - "@visx/shape": "2.17.0", + "@visx/curve": "3.3.0", + "@visx/group": "3.3.0", + "@visx/point": "3.3.0", + "@visx/scale": "3.5.0", + "@visx/shape": "3.5.0", "classnames": "^2.3.1", "prop-types": "^15.6.2" }, "dependencies": { + "@visx/group": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/group/-/group-3.3.0.tgz", + "integrity": "sha512-yKepDKwJqlzvnvPS0yDuW13XNrYJE4xzT6xM7J++441nu6IybWWwextyap8ey+kU651cYDb+q1Oi6aHvQwyEyw==", + "requires": { + "@types/react": "*", + "classnames": "^2.3.1", + "prop-types": "^15.6.2" + } + }, "@visx/point": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.17.0.tgz", - "integrity": "sha512-fUdGQBLGaSVbFTbQ6k+1nPisbqYjTjAdo9FhlwLd3W3uyXN/39Sx2z3N2579sVNBDzmCKdYNQIU0HC+/3Vqo6w==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/point/-/point-3.3.0.tgz", + "integrity": "sha512-03eBBIJarkmX79WbeEGTUZwmS5/MUuabbiM9KfkGS9pETBTWkp1DZtEHZdp5z34x5TDQVLSi0rk1Plg3/8RtDg==" } } }, @@ -24818,17 +28997,64 @@ "@visx/scale": "2.17.0", "classnames": "^2.3.1", "prop-types": "^15.5.10" + }, + "dependencies": { + "@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@visx/scale": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.17.0.tgz", + "integrity": "sha512-ok0RUOSp9VxZzuwo/1I9nsxZxeAdU6wsvIb+cEyMrCuDwm79wzaioSkafAGSb39cKYNrGobFlA3vUd7+JZPCaw==", + "requires": { + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-time": "^2.0.0", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-time": "^2.1.1" + } + } + } + }, + "@visx/mock-data": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-3.3.0.tgz", + "integrity": "sha512-yb5R/tAU8fjwRSc5VL1UPYbkD+BoYjXUorblE3/oDcSfFrOvpRMZzSaYCBbZ6jtllge3Ks6QVzwyUUj1/xweqQ==", + "requires": { + "@types/d3-random": "^2.2.0", + "d3-random": "^2.2.2" + }, + "dependencies": { + "d3-random": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-2.2.2.tgz", + "integrity": "sha512-0D9P8TRj6qDAtHhRQn6EfdOtHMfsUWanl3yb/84C4DqpZ+VsgfI5iTVRNRbELCfNvRfpMr8OrqqUTQ6ANGCijw==" + } } }, "@visx/point": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.6.0.tgz", - "integrity": "sha512-amBi7yMz4S2VSchlPdliznN41TuES64506ySI22DeKQ+mc1s1+BudlpnY90sM1EIw4xnqbKmrghTTGfy6SVqvQ==" + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/point/-/point-2.17.0.tgz", + "integrity": "sha512-fUdGQBLGaSVbFTbQ6k+1nPisbqYjTjAdo9FhlwLd3W3uyXN/39Sx2z3N2579sVNBDzmCKdYNQIU0HC+/3Vqo6w==" }, "@visx/responsive": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-2.10.0.tgz", - "integrity": "sha512-NssDPpuUYp7hqVISuYkKZ5zk6ob0++RdTIaUjRcUdyFEbvzb9+zIb8QToOkvI90L2EC/MY4Jx0NpDbEe79GpAw==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-2.17.0.tgz", + "integrity": "sha512-3dY2shGbQnoknIRv3Vfnwsy3ZA8Q5Q/rYnTLiokWChYRfNC8NMPoX9mprEeb/gMAxtKjaLn3zcCgd8R+eetxIQ==", "requires": { "@juggle/resize-observer": "^3.3.1", "@types/lodash": "^4.14.172", @@ -24838,35 +29064,42 @@ } }, "@visx/scale": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.17.0.tgz", - "integrity": "sha512-ok0RUOSp9VxZzuwo/1I9nsxZxeAdU6wsvIb+cEyMrCuDwm79wzaioSkafAGSb39cKYNrGobFlA3vUd7+JZPCaw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-3.5.0.tgz", + "integrity": "sha512-xo3zrXV2IZxrMq9Y9RUVJUpd93h3NO/r/y3GVi5F9AsbOzOhsLIbsPkunhO9mpUSR8LZ9TiumLEBrY+3frRBSg==", "requires": { - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-time": "^2.0.0", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-time": "^2.1.1" + "@visx/vendor": "3.5.0" } }, "@visx/shape": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.17.0.tgz", - "integrity": "sha512-c2uun6f9souLIyUx+WLetG2JSJ4hF3dJqs1yoFZuO5BLNcU35LTCbqvEq10hLPB7TLqkA0s3jWt/rpE4M3S0Mw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-3.5.0.tgz", + "integrity": "sha512-DP3t9jBQ7dSE3e6ptA1xO4QAIGxO55GrY/6P+S6YREuQGjZgq20TLYLAsiaoPEzFSS4tp0m12ZTPivWhU2VBTw==", "requires": { "@types/d3-path": "^1.0.8", "@types/d3-shape": "^1.3.1", "@types/lodash": "^4.14.172", "@types/react": "*", - "@visx/curve": "2.17.0", - "@visx/group": "2.17.0", - "@visx/scale": "2.17.0", + "@visx/curve": "3.3.0", + "@visx/group": "3.3.0", + "@visx/scale": "3.5.0", "classnames": "^2.3.1", "d3-path": "^1.0.5", "d3-shape": "^1.2.0", "lodash": "^4.17.21", "prop-types": "^15.5.10" + }, + "dependencies": { + "@visx/group": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@visx/group/-/group-3.3.0.tgz", + "integrity": "sha512-yKepDKwJqlzvnvPS0yDuW13XNrYJE4xzT6xM7J++441nu6IybWWwextyap8ey+kU651cYDb+q1Oi6aHvQwyEyw==", + "requires": { + "@types/react": "*", + "classnames": "^2.3.1", + "prop-types": "^15.6.2" + } + } } }, "@visx/text": { @@ -24883,183 +29116,251 @@ } }, "@visx/tooltip": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.10.0.tgz", - "integrity": "sha512-6Zrd79MIEfyuLBcZ1ypSeAkpQc8oLRNB7FQnegzl3Lje4LK5lJtuf5ST0mwK6G2Uv+GlOW9REJ6VK4gfAGkq9A==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.17.0.tgz", + "integrity": "sha512-+dMHURP9NqSFZLomMUnoVYjRs+I2qcOw1yYvLtTp/4GUAFRMSUJoSJeuLwng1VBIgCEB95xuQ95NgGID4qzPxA==", "requires": { "@types/react": "*", - "@visx/bounds": "2.10.0", + "@visx/bounds": "2.17.0", "classnames": "^2.3.1", "prop-types": "^15.5.10", "react-use-measure": "^2.0.4" } }, + "@visx/vendor": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@visx/vendor/-/vendor-3.5.0.tgz", + "integrity": "sha512-yt3SEZRVmt36+APsCISSO9eSOtzQkBjt+QRxNRzcTWuzwMAaF3PHCCSe31++kkpgY9yFoF+Gfes1TBe5NlETiQ==", + "requires": { + "@types/d3-array": "3.0.3", + "@types/d3-color": "3.1.0", + "@types/d3-delaunay": "6.0.1", + "@types/d3-format": "3.0.1", + "@types/d3-geo": "3.1.0", + "@types/d3-interpolate": "3.0.1", + "@types/d3-scale": "4.0.2", + "@types/d3-time": "3.0.0", + "@types/d3-time-format": "2.1.0", + "d3-array": "3.2.1", + "d3-color": "3.1.0", + "d3-delaunay": "6.0.2", + "d3-format": "3.1.0", + "d3-geo": "3.1.0", + "d3-interpolate": "3.0.1", + "d3-scale": "4.0.2", + "d3-time": "3.1.0", + "d3-time-format": "4.1.0", + "internmap": "2.0.3" + }, + "dependencies": { + "@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==", + "requires": { + "internmap": "1 - 2" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + } + } + }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, - "requires": { - "envinfo": "^7.7.3" - } + "requires": {} }, "@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "requires": {} }, @@ -25120,6 +29421,24 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "@zag-js/dom-query": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.16.0.tgz", + "integrity": "sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ==" + }, + "@zag-js/element-size": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.10.5.tgz", + "integrity": "sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w==" + }, + "@zag-js/focus-visible": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.16.0.tgz", + "integrity": "sha512-a7U/HSopvQbrDU4GLerpqiMcHKEkQkNPeDZJWz38cw/6Upunh41GjHetq5TB84hxyCaDzJ6q2nEdNoBQfC0FKA==", + "requires": { + "@zag-js/dom-query": "0.16.0" + } + }, "abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -25142,9 +29461,9 @@ } }, "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true }, "acorn-globals": { @@ -25165,10 +29484,10 @@ } } }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "requires": {} }, @@ -25289,6 +29608,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -25507,12 +29827,6 @@ "apollo-server-env": "^4.2.1" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -25523,17 +29837,17 @@ } }, "aria-hidden": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.1.tgz", - "integrity": "sha512-PN344VAf9j1EAi+jyVHOJ8XidQdPVssGco39eNcsGdM4wcsILtxrKLkbuiMfLWYROK1FjRQasMWCBttrhjnr6A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", "requires": { "tslib": "^2.0.0" }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -25629,6 +29943,25 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", + "dev": true, + "requires": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -25656,18 +29989,21 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, "babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -25859,11 +30195,6 @@ "babel-preset-current-node-syntax": "^1.0.0" } }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -25930,23 +30261,18 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, + "baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "devOptional": true + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, - "bcp-47": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", - "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -25979,22 +30305,22 @@ } }, "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", + "bytes": "~3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "dependencies": { "depd": { @@ -26003,15 +30329,15 @@ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" } }, "inherits": { @@ -26020,30 +30346,20 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" } } }, "bonjour-service": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.12.tgz", - "integrity": "sha512-pMmguXYCu63Ug37DluMKEHdxc+aaIf/ay4YbF8Gxtba+9d3u+rmEWy61VK3Z3hp8Rskok3BunHYnG0dUHAsblw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", "dev": true, "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.4" - }, - "dependencies": { - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - } + "multicast-dns": "^7.2.5" } }, "boolbase": { @@ -26053,9 +30369,9 @@ "dev": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -26063,12 +30379,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-process-hrtime": { @@ -26078,15 +30394,16 @@ "dev": true }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "devOptional": true, "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" } }, "bser": { @@ -26111,7 +30428,7 @@ "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "dev": true }, "buffer-from": { @@ -26120,11 +30437,26 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "requires": { + "run-applescript": "^7.0.0" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, + "bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -26143,12 +30475,33 @@ } }, "call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + } + }, + "call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" } }, "callsites": { @@ -26178,12 +30531,12 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true + "devOptional": true }, "caniuse-lite": { - "version": "1.0.30001375", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", + "version": "1.0.30001768", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001768.tgz", + "integrity": "sha512-qY3aDRZC5nWPgHUgIB84WL+nySuo19wk0VJpp/XI9T34lrvkyhRvNVOFJOp2kxClQhiFBu+TaUSudf6oa3vkSA==", "devOptional": true }, "capture-exit": { @@ -26199,6 +30552,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -26211,12 +30565,6 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", @@ -26336,20 +30684,14 @@ "dev": true }, "cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", "dev": true, "requires": { "colors": "1.4.0" } }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -26404,6 +30746,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -26411,7 +30754,13 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==" }, "colorette": { "version": "2.0.16", @@ -26435,9 +30784,9 @@ } }, "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true }, "commondir": { @@ -26456,38 +30805,40 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, "requires": { "mime-db": ">= 1.43.0 < 2" } }, "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true + "negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, "compute-scroll-into-view": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz", - "integrity": "sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.0.3.tgz", + "integrity": "sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A==" }, "concat-map": { "version": "0.0.1", @@ -26517,9 +30868,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { "version": "1.7.0", @@ -26530,9 +30881,9 @@ } }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" }, "cookie-signature": { "version": "1.0.6", @@ -26552,31 +30903,91 @@ "dev": true }, "copy-to-clipboard": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", - "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", "requires": { "toggle-selection": "^1.0.6" } }, - "core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "copy-webpack-plugin": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-14.0.0.tgz", + "integrity": "sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==", "dev": true, "requires": { - "browserslist": "^4.21.3", - "semver": "7.0.0" + "glob-parent": "^6.0.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^7.0.3", + "tinyglobby": "^0.2.12" }, "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "serialize-javascript": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true } } }, + "core-js-compat": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", + "dev": true, + "requires": { + "browserslist": "^4.22.2" + } + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -26605,16 +31016,10 @@ "yaml": "^1.10.0" } }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -26685,9 +31090,9 @@ } }, "csstype": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", - "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "d3": { "version": "7.8.0", @@ -27027,9 +31432,9 @@ } }, "date-fns": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", - "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "dev": true }, "debounce": { @@ -27064,9 +31469,14 @@ "dev": true }, "deeks": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/deeks/-/deeks-2.5.1.tgz", - "integrity": "sha512-fqrBeUz7f1UqaXDRzVB5RG2EfPk15HJRrb2pMZj8mLlSTtz4tRPsK5leFOskoHFPuyZ6+7aRM9j657fvXLkJ7Q==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.2.1.tgz", + "integrity": "sha512-D/o0k3pCG1aI1cxb/dDiWmtMc4Rh7ZQBybXpfMsw9Rbtqwg8kUA9SpYkWcw0pAUjZSnPm8MluctiS0o68r69jQ==" + }, + "deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" }, "deep-is": { "version": "0.1.3", @@ -27079,61 +31489,22 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "dev": true, "requires": { - "execa": "^5.0.0" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - } + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" } }, + "default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true + }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -27143,10 +31514,21 @@ "clone": "^1.0.2" } }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true }, "define-properties": { @@ -27267,12 +31649,6 @@ "wrappy": "1" } }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", @@ -27288,25 +31664,19 @@ "path-type": "^4.0.0" } }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, "dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "requires": { "@leichtgewicht/ip-codec": "^2.0.1" } }, "doc-path": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-3.0.2.tgz", - "integrity": "sha512-VRlA2OKSjTbHWj6wmSanxJ338fE/YN8pqmZ0FIWK5JWkIJMFRc4KmD35JtOrnjvVG0WrzOtDDNHx1lN1tkb+lA==" + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.4.tgz", + "integrity": "sha512-yw5D++UCIB6a033PvQaUvSpW2QuKW0+DOId763n0Q4z3brxS7G8oQr8yBQ1nQFkognKrAVrV6I55TLeU9cfXTg==" }, "doctrine": { "version": "3.0.0", @@ -27416,9 +31786,25 @@ "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" }, "dset": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", - "integrity": "sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "dev": true + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, "ecdsa-sig-formatter": { @@ -27436,9 +31822,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.4.213", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.213.tgz", - "integrity": "sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg==", + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "devOptional": true }, "emittery": { @@ -27474,13 +31860,13 @@ } }, "enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, "requires": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" } }, "enquirer": { @@ -27499,9 +31885,9 @@ "dev": true }, "envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "dev": true }, "error-ex": { @@ -27550,12 +31936,42 @@ "unbox-primitive": "^1.0.2" } }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -27576,10 +31992,43 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" + } + }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "devOptional": true }, "escape-html": { @@ -27590,7 +32039,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escodegen": { "version": "2.0.0", @@ -27751,9 +32201,9 @@ "dev": true }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -28056,6 +32506,13 @@ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "optional": true + }, "espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -28130,7 +32587,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "eventemitter3": { "version": "4.0.7", @@ -28166,9 +32623,9 @@ }, "dependencies": { "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -28292,38 +32749,38 @@ } }, "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", - "content-disposition": "0.5.4", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -28334,22 +32791,10 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, - "http-errors": { + "encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" }, "safe-buffer": { "version": "5.2.1", @@ -28384,17 +32829,6 @@ } } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -28483,18 +32917,6 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" - }, - "dependencies": { - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - } } }, "fast-json-stable-stringify": { @@ -28515,6 +32937,12 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true + }, "fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", @@ -28548,15 +32976,6 @@ "bser": "2.1.1" } }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -28567,21 +32986,21 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -28589,6 +33008,11 @@ "unpipe": "~1.0.0" }, "dependencies": { + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -28657,15 +33081,6 @@ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -28688,70 +33103,88 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "focus-lock": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.2.tgz", - "integrity": "sha512-YtHxjX7a0IC0ZACL5wsX8QdncXofWpGPNoVMuI/nZUrPGp6LmNI6+D5j0pPj+v8Kw5EpweA+T5yImK0rnWf7oQ==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.5.tgz", + "integrity": "sha512-QFaHbhv9WPUeLYBDe/PAuLKJ4Dd9OPvKs9xZBr3yLXnUrDNaVXKu2baDBXe3naPY30hgHYSsf2JW4jzas2mDEQ==", "requires": { "tslib": "^2.0.3" }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, "follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true }, + "for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "requires": { + "is-callable": "^1.2.7" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, "requires": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0", "qs": "^6.11.0" - }, - "dependencies": { - "qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - } } }, "formik": { @@ -28820,25 +33253,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-readdir-recursive": { "version": "1.1.0", @@ -28860,9 +33275,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { "version": "1.1.5", @@ -28888,12 +33303,6 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, - "fuzzaldrin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", - "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=", - "dev": true - }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -28907,13 +33316,20 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" } }, "get-nonce": { @@ -28927,6 +33343,15 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -28995,7 +33420,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "devOptional": true + "dev": true }, "globby": { "version": "6.1.0", @@ -29018,10 +33443,15 @@ } } }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "graphql": { @@ -29090,29 +33520,30 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "requires": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" } }, "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" }, "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "requires": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" } }, "has-value": { @@ -29167,36 +33598,25 @@ } } }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true - }, "hey-listen": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" }, - "history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -29264,12 +33684,6 @@ "whatwg-encoding": "^1.0.5" } }, - "html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "dev": true - }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -29330,6 +33744,35 @@ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", "dev": true }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } + } + }, "http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", @@ -29376,9 +33819,9 @@ } }, "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "dev": true, "requires": { "@types/http-proxy": "^1.17.8", @@ -29421,6 +33864,12 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, + "hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -29448,9 +33897,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -29539,78 +33988,6 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -29628,11 +34005,22 @@ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" }, "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true }, + "intro.js": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/intro.js/-/intro.js-7.2.0.tgz", + "integrity": "sha512-qbMfaB70rOXVBceIWNYnYTpVTiZsvQh/MIkfdQbpA9di9VBfj1GigUPfcCv3aOfsbrtPcri8vTLTA4FcEDcHSQ==" + }, + "intro.js-react": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/intro.js-react/-/intro.js-react-1.0.0.tgz", + "integrity": "sha512-zR8pbTyX20RnCZpJMc0nuHBpsjcr1wFkj3ZookV6Ly4eE/LGpFTQwPsaA61Cryzwiy/tTFsusf4hPU9NpI9UOg==", + "requires": {} + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -29666,22 +34054,6 @@ } } }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -29717,9 +34089,9 @@ "dev": true }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true }, "is-ci": { @@ -29765,12 +34137,6 @@ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -29794,7 +34160,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", - "dev": true + "dev": true, + "optional": true }, "is-extendable": { "version": "0.1.1", @@ -29829,18 +34196,46 @@ "is-extglob": "^2.1.1" } }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + }, + "dependencies": { + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + } + } + }, "is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true }, + "is-lite": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz", + "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw==" + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, + "is-network-error": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", + "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -29856,6 +34251,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-observable": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz", + "integrity": "sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==", + "dev": true + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -29944,6 +34345,15 @@ "has-symbols": "^1.0.1" } }, + "is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.16" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -29970,14 +34380,16 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } }, "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, "isexe": { "version": "2.0.0", @@ -30101,7 +34513,17 @@ "iterall": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", - "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", + "dev": true + }, + "jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2" + } }, "jest": { "version": "26.6.3", @@ -31412,9 +35834,9 @@ } }, "jest-worker": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.2.tgz", - "integrity": "sha512-0QMy/zPovLfUPyHuOuuU4E+kGACXXE84nRnq6lBVI9GJg5DCBiA97SATi+ZP8CpiJwEQy1oCPjRBf8AnLjN+Ag==", + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "requires": { "@types/node": "*", @@ -31439,15 +35861,26 @@ } } }, + "jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true + }, + "js-sha256": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz", + "integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -31469,7 +35902,7 @@ "decimal.js": "^10.2.1", "domexception": "^2.0.1", "escodegen": "^2.0.0", - "form-data": "^3.0.0", + "form-data": "3.0.4", "html-encoding-sniffer": "^2.0.1", "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", @@ -31487,33 +35920,41 @@ "whatwg-url": "^8.5.0", "ws": "^7.4.6", "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" + } + } } }, "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "devOptional": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==" }, "json-2-csv": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-3.17.1.tgz", - "integrity": "sha512-i6QynVy42GGMgY8fYde0mp6nYteptvk8oJsphOLiT3CITzw7NBBAiRwHV35kDOBii/elDQe1HCWLqaBPJ3istQ==", + "version": "5.5.11", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.11.tgz", + "integrity": "sha512-kVuwgVL7rfad9ETf02ZZxJPuMR5ZSUn139+T34BfmVxYhb/IsAIm/LzEeQ8YLJmXfuQ5z7LUAFrgPZZM6VLJPw==", "requires": { - "deeks": "2.5.1", - "doc-path": "3.0.2" + "deeks": "3.2.1", + "doc-path": "4.1.4" } }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema-traverse": { "version": "0.4.1", @@ -31533,16 +35974,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "devOptional": true }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -31583,23 +36014,23 @@ } }, "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dev": true, "requires": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dev": true, "requires": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -31615,11 +36046,21 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "launch-editor": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz", + "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==", + "dev": true, + "requires": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "devOptional": true }, "levn": { "version": "0.4.1", @@ -31637,9 +36078,9 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true }, "loader-utils": { @@ -31653,37 +36094,21 @@ "json5": "^2.1.2" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==" }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -31851,12 +36276,6 @@ } } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "make-plural": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.1.0.tgz", @@ -31891,29 +36310,284 @@ "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz", "integrity": "sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==" }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "memfs": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", - "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", - "dev": true, - "requires": { - "fs-monkey": "1.0.3" + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.2.tgz", + "integrity": "sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==", + "dev": true, + "requires": { + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-to-fsa": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "dependencies": { + "@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "requires": {} + }, + "@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", + "dev": true, + "requires": {} + }, + "@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "dev": true, + "requires": {} + }, + "@jsonjoy.com/fs-core": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.2.tgz", + "integrity": "sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==", + "dev": true, + "requires": { + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "thingies": "^2.5.0" + } + }, + "@jsonjoy.com/fs-fsa": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.2.tgz", + "integrity": "sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==", + "dev": true, + "requires": { + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "thingies": "^2.5.0" + } + }, + "@jsonjoy.com/fs-node": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.2.tgz", + "integrity": "sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==", + "dev": true, + "requires": { + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", + "glob-to-regex.js": "^1.0.0", + "thingies": "^2.5.0" + } + }, + "@jsonjoy.com/fs-node-builtins": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.2.tgz", + "integrity": "sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==", + "dev": true, + "requires": {} + }, + "@jsonjoy.com/fs-node-to-fsa": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.2.tgz", + "integrity": "sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==", + "dev": true, + "requires": { + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2" + } + }, + "@jsonjoy.com/fs-node-utils": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.2.tgz", + "integrity": "sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==", + "dev": true, + "requires": { + "@jsonjoy.com/fs-node-builtins": "4.57.2" + } + }, + "@jsonjoy.com/fs-print": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.2.tgz", + "integrity": "sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==", + "dev": true, + "requires": { + "@jsonjoy.com/fs-node-utils": "4.57.2", + "tree-dump": "^1.1.0" + } + }, + "@jsonjoy.com/fs-snapshot": { + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.2.tgz", + "integrity": "sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==", + "dev": true, + "requires": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "dependencies": { + "@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "dev": true, + "requires": {} + }, + "@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "dev": true, + "requires": {} + }, + "@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "dev": true, + "requires": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + } + }, + "@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "dev": true, + "requires": { + "@jsonjoy.com/util": "17.67.0" + } + }, + "@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "dev": true, + "requires": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + } + } + } + }, + "@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "dev": true, + "requires": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "dependencies": { + "@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "requires": {} + } + } + }, + "@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "requires": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + } + }, + "@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "dev": true, + "requires": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "dependencies": { + "@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "requires": {} + } + } + }, + "glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "dev": true, + "requires": {} + }, + "thingies": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", + "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", + "dev": true, + "requires": {} + }, + "tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "dev": true, + "requires": {} + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } } }, - "memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" }, "merge-stream": { "version": "2.0.0", @@ -31927,24 +36601,19 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, - "messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.3", + "picomatch": "^2.3.1" } }, "mime": { @@ -31972,9 +36641,9 @@ "dev": true }, "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.1.tgz", + "integrity": "sha512-8lqe85PkqQJzIcs2iD7xW/WSxcncC3/DPVbTOafKNJDIMXwGfwXS350mH4SJslomntN2iYtFBuC0yNO3CEap6g==", "dev": true, "requires": { "dom-walk": "^0.1.0" @@ -31993,9 +36662,9 @@ "dev": true }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -32007,6 +36676,12 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -32028,11 +36703,10 @@ } } }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" }, "mq-polyfill": { "version": "1.1.8", @@ -32055,12 +36729,6 @@ "thunky": "^1.0.2" } }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, "nanoclone": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", @@ -32161,21 +36829,6 @@ } } }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true - }, - "node-gettext": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", - "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", - "dev": true, - "requires": { - "lodash.get": "^4.4.2" - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -32216,21 +36869,21 @@ } }, "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "devOptional": true }, "nodemon": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz", - "integrity": "sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==", + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", "dev": true, "requires": { "chokidar": "^3.5.2", "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", "semver": "^5.7.1", "simple-update-notifier": "^1.0.7", @@ -32368,9 +37021,9 @@ } }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" }, "object-keys": { "version": "1.1.1", @@ -32451,6 +37104,12 @@ "es-abstract": "^1.19.1" } }, + "observable-fns": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz", + "integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==", + "dev": true + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -32466,10 +37125,9 @@ } }, "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==" }, "once": { "version": "1.4.0", @@ -32490,14 +37148,15 @@ } }, "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" } }, "optimism": { @@ -32590,12 +37249,6 @@ } } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -32617,15 +37270,6 @@ "p-try": "^2.0.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, "p-map": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", @@ -32633,12 +37277,13 @@ "dev": true }, "p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", "dev": true, "requires": { - "@types/retry": "0.12.0", + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", "retry": "^0.13.1" } }, @@ -32648,10 +37293,10 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "papaparse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.0.tgz", - "integrity": "sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==", + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, "param-case": { @@ -32681,13 +37326,13 @@ } }, "parse-json": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz", - "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", + "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, @@ -32755,10 +37400,28 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "requires": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "dependencies": { + "lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true + } + } + }, "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" }, "path-type": { "version": "4.0.0", @@ -32766,15 +37429,14 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "devOptional": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true }, "pify": { @@ -32807,25 +37469,38 @@ "node-modules-regexp": "^1.0.0" } }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", "dev": true, "requires": { - "find-up": "^3.0.0" + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } } }, - "plurals-cldr": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/plurals-cldr/-/plurals-cldr-1.0.4.tgz", - "integrity": "sha512-4nLXqtel7fsCgzi8dvRZvUjfL8SXpP982sKg7b2TgpnR8rDnes06iuQ83trQ/+XdtyMIQkBBbKzX6x97eLfsJQ==", - "dev": true - }, "pofile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.0.tgz", - "integrity": "sha512-6XYcNkXWGiJ2CVXogTP7uJ6ZXQCldYLZc16wgRp8tqRaBTTyIfF+TUT3EQJPXTLAT7OTPpTAoaFdoXKfaTRU1w==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.4.tgz", + "integrity": "sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g==", "dev": true }, "popmotion": { @@ -32846,12 +37521,23 @@ } } }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -32971,12 +37657,12 @@ } }, "pseudolocale": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-1.2.0.tgz", - "integrity": "sha512-k0OQFvIlvpRdzR0dPVrrbWX7eE9EaZ6gpZtTlFSDi1Gf9tMy9wiANCNu7JZ0drcKgUri/39a2mBbH0goiQmrmQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-2.1.0.tgz", + "integrity": "sha512-af5fsrRvVwD+MBasBJvuDChT0KDqT0nEwD9NTgbtHJ16FKomWac9ua0z6YVNB4G9x9IOaiGWym62aby6n4tFMA==", "dev": true, "requires": { - "commander": "*" + "commander": "^10.0.0" } }, "psl": { @@ -33007,12 +37693,35 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "requires": { + "tslib": "^2.8.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "dev": true + }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.1.0" } }, "querystringify": { @@ -33027,35 +37736,20 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "dependencies": { "depd": { @@ -33064,15 +37758,15 @@ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" } }, "inherits": { @@ -33081,9 +37775,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" } } }, @@ -33103,25 +37797,6 @@ "@babel/runtime": "^7.12.13" } }, - "react-diff-viewer-continued": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-3.2.6.tgz", - "integrity": "sha512-GrzyqQnjIMoej+jMjWvtVSsQqhXgzEGqpXlJ2dAGfOk7Q26qcm8Gu6xtI430PBUyZsERe8BJSQf+7VZZo8IBNQ==", - "requires": { - "@emotion/css": "^11.10.5", - "classnames": "^2.3.1", - "diff": "^5.1.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.8.1" - }, - "dependencies": { - "diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" - } - } - }, "react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -33144,6 +37819,57 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, + "react-floater": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz", + "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==", + "requires": { + "deepmerge": "^4.3.1", + "is-lite": "^0.8.2", + "popper.js": "^1.16.0", + "prop-types": "^15.8.1", + "tree-changes": "^0.9.1" + }, + "dependencies": { + "@gilbarbara/deep-equal": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==" + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "is-lite": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz", + "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==" + }, + "tree-changes": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz", + "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==", + "requires": { + "@gilbarbara/deep-equal": "^0.1.1", + "is-lite": "^0.8.2" + } + } + } + }, + "react-focus-lock": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.12.1.tgz", + "integrity": "sha512-lfp8Dve4yJagkHiFrC1bGtib3mF2ktqwPJw4/WGcgPW+pJ/AVQA5X2vI7xgp13FcxFEpYBBHpXai/N2DBNC0Jw==", + "requires": { + "@babel/runtime": "^7.0.0", + "focus-lock": "^1.3.5", + "prop-types": "^15.6.2", + "react-clientside-effect": "^1.2.6", + "use-callback-ref": "^1.3.2", + "use-sidecar": "^1.1.2" + } + }, "react-hot-loader": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.0.tgz", @@ -33188,11 +37914,47 @@ } } }, + "react-innertext": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz", + "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==", + "requires": {} + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-joyride": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.8.2.tgz", + "integrity": "sha512-2QY8HB1G0I2OT0PKMUz7gg2HAjdkG2Bqi13r0Bb1V16PAwfb9khn4wWBTOJsGsjulbAWiQ3/0YrgNUHGFmuifw==", + "requires": { + "@gilbarbara/deep-equal": "^0.3.1", + "deep-diff": "^1.0.2", + "deepmerge": "^4.3.1", + "is-lite": "^1.2.1", + "react-floater": "^0.7.9", + "react-innertext": "^1.1.5", + "react-is": "^16.13.1", + "scroll": "^3.0.1", + "scrollparent": "^2.1.0", + "tree-changes": "^0.11.2", + "type-fest": "^4.18.2" + }, + "dependencies": { + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "type-fest": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.21.0.tgz", + "integrity": "sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==" + } + } + }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -33212,70 +37974,56 @@ "prop-types": "^15.7.2" } }, - "react-remove-scroll-bar": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.3.tgz", - "integrity": "sha512-i9GMNWwpz8XpUpQ6QlevUtFjHGqnPG4Hxs+wlIJntu/xcsZVEpJcIV71K3ZkqNy2q3GfgvkD7y6t/Sv8ofYSbw==", + "react-remove-scroll": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.9.tgz", + "integrity": "sha512-bvHCLBrFfM2OgcrpPY2YW84sPdS2o2HKWJUf1xGyGLnSoEnOTOBpahIarjRuYtN0ryahCeP242yf+5TrBX/pZA==", "requires": { + "react-remove-scroll-bar": "^2.3.6", "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, - "react-router": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.3.tgz", - "integrity": "sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "requires": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" }, "dependencies": { - "mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "requires": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - } - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - } + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, + "react-router": { + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.4.tgz", + "integrity": "sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==", + "requires": { + "@remix-run/router": "1.23.3" + } + }, "react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng==", + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz", + "integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==", "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.3", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.23.3", + "react-router": "6.30.4" } }, "react-shallow-renderer": { @@ -33299,9 +38047,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -33359,12 +38107,12 @@ } }, "rechoir": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", - "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "requires": { - "resolve": "^1.9.0" + "resolve": "^1.20.0" } }, "redent": { @@ -33402,6 +38150,12 @@ "balanced-match": "^1.0.0" } }, + "reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -33418,9 +38172,9 @@ } }, "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "regenerator-transform": { "version": "0.15.0", @@ -33588,11 +38342,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, - "resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -33647,10 +38396,10 @@ "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "dev": true }, "run-parallel": { @@ -33667,15 +38416,6 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, - "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -33864,6 +38604,16 @@ "ajv-keywords": "^3.4.1" } }, + "scroll": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz", + "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==" + }, + "scrollparent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz", + "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA==" + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -33871,12 +38621,13 @@ "dev": true }, "selfsigned": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", - "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", + "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", "dev": true, "requires": { - "node-forge": "^1" + "@peculiar/x509": "^1.14.2", + "pkijs": "^3.3.3" } }, "semver": { @@ -33886,9 +38637,9 @@ "dev": true }, "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "requires": { "debug": "2.6.9", "depd": "2.0.0", @@ -33910,23 +38661,6 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -33939,15 +38673,6 @@ } } }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -33984,14 +38709,21 @@ } }, "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "requires": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" + }, + "dependencies": { + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + } } }, "set-blocking": { @@ -34000,6 +38732,20 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -34029,13 +38775,28 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } } }, "shallow-clone": { @@ -34068,6 +38829,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", + "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "dev": true + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -34076,13 +38843,47 @@ "optional": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" } }, "signal-exit": { @@ -34092,9 +38893,9 @@ "dev": true }, "simple-update-notifier": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz", - "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", "dev": true, "requires": { "semver": "~7.0.0" @@ -34391,12 +39192,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -34422,12 +39223,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -34535,6 +39336,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -34582,6 +39394,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -34636,30 +39457,6 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, - "subscriptions-transport-ws": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", - "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "requires": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" - }, - "dependencies": { - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - } - } - }, "superagent": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.0.tgz", @@ -34670,7 +39467,7 @@ "cookiejar": "^2.1.3", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", + "form-data": "4.0.4", "formidable": "^2.0.1", "methods": "^1.1.2", "mime": "2.6.0", @@ -34688,17 +39485,6 @@ "ms": "2.1.2" } }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -34736,6 +39522,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -34817,9 +39604,9 @@ } }, "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true }, "terminal-link": { @@ -34833,13 +39620,14 @@ } }, "terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "dependencies": { @@ -34848,44 +39636,59 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true } } }, "terser-webpack-plugin": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", - "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "dev": true, "requires": { - "jest-worker": "^27.0.6", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" }, "dependencies": { - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true + }, + "schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } } } }, @@ -34906,18 +39709,42 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "threads": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/threads/-/threads-1.7.0.tgz", + "integrity": "sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==", + "dev": true, + "requires": { + "callsites": "^3.1.0", + "debug": "^4.2.0", + "is-observable": "^2.1.0", + "observable-fns": "^0.6.1", + "tiny-worker": ">= 2" + }, + "dependencies": { + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -34934,13 +39761,39 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "tiny-worker": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", + "integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==", + "dev": true, + "optional": true, + "requires": { + "esm": "^3.2.25" + } + }, + "tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "dependencies": { + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "requires": {} + }, + "picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true + } } }, "tmpl": { @@ -34949,10 +39802,24 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "dev": true, + "requires": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } }, "to-object-path": { "version": "0.3.0", @@ -34998,7 +39865,7 @@ "toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, "toidentifier": { "version": "1.0.1", @@ -35048,6 +39915,15 @@ "punycode": "^2.1.1" } }, + "tree-changes": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.2.tgz", + "integrity": "sha512-4gXlUthrl+RabZw6lLvcCDl6KfJOCmrC16BC5CRdut1EAH509Omgg0BfKLY+ViRlzrvYOTWR0FMS2SQTwzumrw==", + "requires": { + "@gilbarbara/deep-equal": "^0.3.1", + "is-lite": "^1.2.0" + } + }, "ts-invariant": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", @@ -35063,20 +39939,6 @@ } } }, - "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - } - }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -35114,6 +39976,15 @@ "tslib": "^1.8.1" } }, + "tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -35144,6 +40015,17 @@ "mime-types": "~2.1.24" } }, + "typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + } + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -35154,10 +40036,10 @@ } }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true, + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, "peer": true }, "unbox-primitive": { @@ -35223,12 +40105,6 @@ "set-value": "^2.0.1" } }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -35281,13 +40157,13 @@ } }, "update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "devOptional": true, "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" } }, "uri-js": { @@ -35322,17 +40198,17 @@ "dev": true }, "use-callback-ref": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", - "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", "requires": { "tslib": "^2.0.0" }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -35346,9 +40222,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -35410,11 +40286,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "value-or-promise": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", @@ -35453,18 +40324,10 @@ "makeerror": "1.0.x" } }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "requires": { - "loose-envify": "^1.0.0" - } - }, "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -35496,76 +40359,98 @@ "dev": true }, "webpack": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", - "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" }, "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" } } } }, "webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - } } }, "webpack-config-utils": { @@ -35588,28 +40473,29 @@ } }, "webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz", + "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==", "dev": true, "requires": { "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", + "memfs": "^4.43.1", + "mime-types": "^3.0.1", + "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "ajv-keywords": { @@ -35627,76 +40513,105 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true + }, + "mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "requires": { + "mime-db": "^1.54.0" + } + }, "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" } } } }, "webpack-dev-server": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.10.0.tgz", - "integrity": "sha512-7dezwAs+k6yXVFZ+MaL8VnE+APobiO3zvpp3rBHe/HmWQ+avwh0Q3d0xxacOiBybZZ3syTZw9HXzpa3YNbAZDQ==", - "dev": true, - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.5.tgz", + "integrity": "sha512-4wZtCquSuv9CKX8oybo+mqxtxZqWz47uM1Ch94lxowBztOhWCbhqvRbfC/mODOwxgV2brY+JGZpHq58/SuVFYg==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.25", + "@types/express-serve-static-core": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", "colorette": "^2.0.10", - "compression": "^1.7.4", + "compression": "^1.8.1", "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", + "express": "^4.22.1", "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.1", + "http-proxy-middleware": "^2.0.9", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^5.5.0", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" }, "dependencies": { - "@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", "dev": true, "requires": { - "@types/node": "*" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "ajv-keywords": { @@ -35709,9 +40624,9 @@ } }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -35725,9 +40640,9 @@ } }, "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", + "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", "dev": true }, "json-schema-traverse": { @@ -35736,31 +40651,22 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" } }, "ws": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", - "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "requires": {} } @@ -35777,15 +40683,15 @@ } }, "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true }, "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz", + "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==", "dev": true, "requires": { "http-parser-js": ">=0.5.1", @@ -35853,6 +40759,21 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + } + }, "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -35902,6 +40823,43 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -35921,11 +40879,32 @@ } }, "ws": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", - "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "dev": true, "requires": {} }, + "wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "requires": { + "is-wsl": "^3.1.0" + }, + "dependencies": { + "is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "requires": { + "is-inside-container": "^1.0.0" + } + } + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -35969,9 +40948,9 @@ "dev": true }, "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==" }, "yargs": { "version": "15.4.1", @@ -36046,12 +41025,6 @@ } } }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, "yup": { "version": "0.32.11", "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", diff --git a/frontend/package.json b/frontend/package.json index a0bdb7806..bea5a18e8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,10 +3,12 @@ "version": "0.1.0", "scripts": { "build": "webpack --env production --config webpack.config.js", + "build:dev": "webpack --env development --config webpack.config.js", "dev": "webpack serve --hot --env development", "webpack": "webpack", "start": "NODE_OPTIONS=--openssl-legacy-provider node index.js", - "test": "jest", + "start:dev": "NODE_ENV=development PORT=3300 HOST=127.0.0.1 node index.js", + "test": "jest --silent", "test-coverage": "jest --coverage", "lint": "eslint src", "dbg": "node --inspect-brk node_modules/.bin/jest --runInBand --no-cache", @@ -16,40 +18,48 @@ }, "dependencies": { "@apollo/client": "^3.6.9", - "@babel/runtime": "^7.18.6", - "@chakra-ui/icons": "^1.1.7", - "@chakra-ui/react": "^1.8.8", - "@chakra-ui/system": "^1.0.0", + "@babel/runtime": "^7.27.0", + "@chakra-ui/icons": "^2.1.1", + "@chakra-ui/react": "^2.8.2", + "@chakra-ui/system": "^2.6.2", "@emotion/react": "^11.9.3", "@emotion/styled": "^11.9.3", - "@lingui/react": "^3.14.0", + "@lingui/react": "^5.9.3", "@visx/axis": "^2.17.0", - "@visx/event": "^2.6.0", - "@visx/grid": "^2.17.0", + "@visx/curve": "^3.3.0", + "@visx/event": "^2.17.0", + "@visx/glyph": "^3.3.0", + "@visx/gradient": "^3.3.0", + "@visx/grid": "^3.5.0", "@visx/group": "^2.1.0", "@visx/legend": "^2.17.0", - "@visx/responsive": "^2.10.0", - "@visx/scale": "^2.17.0", - "@visx/shape": "^2.17.0", - "@visx/tooltip": "^2.10.0", - "body-parser": "^1.19.1", + "@visx/mock-data": "^3.3.0", + "@visx/responsive": "^2.17.0", + "@visx/scale": "^3.5.0", + "@visx/shape": "^3.5.0", + "@visx/tooltip": "^2.17.0", + "@visx/vendor": "^3.5.0", + "body-parser": "^1.20.5", + "compression": "^1.8.1", "d3": "^7.8.0", "dotenv": "^16.0.1", - "express": "^4.17.2", + "express": "^4.22.2", "formik": "^2.2.9", "framer-motion": "^4.1.17", + "intro.js": "^7.2.0", + "intro.js-react": "^1.0.0", "isomorphic-unfetch": "^3.1.0", - "json-2-csv": "^3.17.1", + "json-2-csv": "^5.5.11", + "lodash-es": "^4.18.1", "make-plural": "^7.1.0", "prop-types": "^15.8.1", "react": "^18.2.0", - "react-diff-viewer-continued": "^3.2.6", "react-dom": "^18.2.0", "react-error-boundary": "^3.1.4", + "react-joyride": "^2.8.2", "react-phone-input-2": "^2.15.1", - "react-router-dom": "^5.3.3", + "react-router-dom": "^6.30.4", "react-table": "^7.8.0", - "subscriptions-transport-ws": "^0.11.0", "yup": "^0.32.11" }, "devDependencies": { @@ -61,9 +71,10 @@ "@babel/preset-react": "^7.18.6", "@graphql-tools/mock": "^8.7.0", "@graphql-tools/schema": "^10.0.0", - "@lingui/cli": "^3.14.0", - "@lingui/loader": "^3.14.0", - "@lingui/macro": "^3.14.0", + "@lingui/cli": "^5.9.3", + "@lingui/core": "^5.9.3", + "@lingui/loader": "^5.9.3", + "@lingui/macro": "^5.9.3", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.4.0", "@testing-library/react-hooks": "^7.0.2", @@ -73,6 +84,7 @@ "babel-loader": "^8.2.5", "babel-plugin-macros": "^3.1.0", "clean-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^14.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", @@ -94,13 +106,15 @@ "react-test-renderer": "^18.2.0", "source-map-loader": "^4.0.0", "supertest": "^6.2.3", - "webpack": "^5.76.2", - "webpack-cli": "^4.10.0", + "webpack": "^5.105.0", + "webpack-cli": "^5.1.4", "webpack-config-utils": "^2.3.1", - "webpack-dev-server": "^4.9.2" + "webpack-dev-server": "^5.2.5" }, "overrides": { - "colors": "1.4.0" + "colors": "1.4.0", + "form-data@^3.0.0": "3.0.4", + "form-data@^4.0.0": "4.0.4" }, "repository": { "type": "git", @@ -115,7 +129,8 @@ "moduleNameMapper": { "\\.(jpg|jpeg|png|gif|svg)$": "/__mocks__/fileMock.js", "^d3$": "/node_modules/d3/dist/d3.min.js", - "^d3-[\\w]+$": "/node_modules/d3/dist/d3.min.js" + "^d3-[\\w]+$": "/node_modules/d3/dist/d3.min.js", + "^lodash-es$": "lodash" }, "setupFilesAfterEnv": [ "./src/setupTests.js" diff --git a/frontend/src/Server.js b/frontend/src/Server.js index 702a2a6c0..526c3edb3 100644 --- a/frontend/src/Server.js +++ b/frontend/src/Server.js @@ -1,12 +1,35 @@ const { join, resolve } = require('path') const express = require('express') +const compression = require('compression') const bodyParser = require('body-parser') +const fs = require('fs') const staticPath = join(resolve(process.cwd()), 'public') +const frenchHosts = process.env.FRENCH_HOSTS?.split(',') || [] +const isProduction = process.env.TRACKER_PRODUCTION === 'true' + +const baseHtml = fs.readFileSync(resolve(join('public', 'index.html')), 'utf8') + +const htmlByLanguage = { + en: baseHtml.replace( + '', + ``, + ), + fr: baseHtml.replace( + '', + ``, + ), +} + +function isHashed(filePath) { + return /\.[0-9a-f]{8,}\./i.test(filePath) +} + function Server() { const server = express() server.use(bodyParser.json()) + server.use(compression()) server.disable('x-powered-by') server.set('trust proxy', true) @@ -18,11 +41,37 @@ function Server() { res.json({ status: 'ready' }) }) - server.use('/', express.static(staticPath, { maxage: '365d' })) + server.use( + ['/manifest.json', '/robots.txt', '/favicon.ico'], + express.static(staticPath, { maxAge: '1d', index: false }), + ) - server.get('*', (_req, res) => { - res.sendFile(resolve(join('public', 'index.html'))) + server.use( + '/', + express.static(staticPath, { + index: false, + setHeaders(res, filePath) { + if (isHashed(filePath)) { + // filePath contains a hash? Cache for 1 year + res.setHeader('Cache-Control', 'public, max-age=31536000') + } else { + // file not already handled and does not contain a hash? Cache for 1d + res.setHeader('Cache-Control', 'public, max-age=86400') + } + }, + }), + ) + + server.get('*', (req, res) => { + const host = req.hostname + const lang = frenchHosts.includes(host) ? 'fr' : 'en' + + res.set('Cache-Control', 'no-cache') + + res.send(htmlByLanguage[lang]) }) + return server } + module.exports.Server = Server diff --git a/frontend/src/admin/AdminDomainCard.js b/frontend/src/admin/AdminDomainCard.js index cac8ca383..fcdcf002a 100644 --- a/frontend/src/admin/AdminDomainCard.js +++ b/frontend/src/admin/AdminDomainCard.js @@ -1,39 +1,63 @@ import React from 'react' -import { Trans } from '@lingui/macro' -import { array, bool, string } from 'prop-types' -import { Flex, ListItem, Tag, TagLabel, Text } from '@chakra-ui/react' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" +import { any, array, bool, object, string } from 'prop-types' +import { Flex, ListItem, Tag, TagLabel, Text, Tooltip } from '@chakra-ui/react' + +export function AdminDomainCard({ url, tags, assetState, isArchived, rcode, cvdEnrollment, highAvailability, children, ...rest }) { + const assetStateLabels = { + APPROVED: t`Approved`, + DEPENDENCY: t`Dependency`, + MONITOR_ONLY: t`Monitor Only`, + CANDIDATE: t`Candidate`, + REQUIRES_INVESTIGATION: t`Requires Investigation`, + } -export function AdminDomainCard({ url, tags, isHidden, isArchived, rcode, ...rest }) { return ( - + + {children} {url} - {tags?.map((tag, idx) => { + {tags?.map(({ label, description }, idx) => { return ( - - {tag} - + + + {label.toUpperCase()} + + ) })} - {rcode === 'NXDOMAIN' && ( - - NXDOMAIN + {assetState && ( + + {assetStateLabels[assetState]} )} - {isHidden && ( + {cvdEnrollment && cvdEnrollment.status !== 'NOT_ENROLLED' && ( - HIDDEN + CVD {cvdEnrollment.status} + + + )} + {highAvailability && ( + + + HIGH AVAILABILITY )} + {rcode === 'NXDOMAIN' && ( + + NXDOMAIN + + )} {isArchived && ( @@ -49,7 +73,10 @@ export function AdminDomainCard({ url, tags, isHidden, isArchived, rcode, ...res AdminDomainCard.propTypes = { url: string, tags: array, - isHidden: bool, isArchived: bool, rcode: string, + assetState: string, + children: any, + cvdEnrollment: object, + highAvailability: bool, } diff --git a/frontend/src/admin/AdminDomainList.js b/frontend/src/admin/AdminDomainList.js new file mode 100644 index 000000000..11ed4e5d6 --- /dev/null +++ b/frontend/src/admin/AdminDomainList.js @@ -0,0 +1,104 @@ +import React from 'react' +import { IconButton, Text } from '@chakra-ui/react' +import { ListOf } from '../components/ListOf' +import { Trans } from '@lingui/react/macro' +import { EditIcon, MinusIcon } from '@chakra-ui/icons' +import { AdminDomainCard } from './AdminDomainCard' +import { array, bool, func, string } from 'prop-types' + +export function AdminDomainList({ + nodes, + verified, + permission, + setSelectedRemoveProps, + removeOnOpen, + setModalProps, + updateOnOpen, +}) { + return ( + ( + + No Domains + + )} + > + {( + { + id: domainId, + domain, + claimTags, + archived, + rcode, + organizations, + assetState, + cvdEnrollment, + highAvailability, + }, + index, + ) => ( + + + {(!verified || permission === 'SUPER_ADMIN' || rcode === 'NXDOMAIN') && ( + { + setSelectedRemoveProps({ domain, domainId, rcode }) + removeOnOpen() + }} + variant="danger" + px="2" + icon={} + aria-label={'Remove ' + domain} + mr="1" + /> + )} + { + setModalProps({ + archived, + mutation: 'update', + assetState, + tagInputList: claimTags, + editingDomainId: domainId, + editingDomainUrl: domain, + orgCount: organizations.totalCount, + cvdEnrollment, + highAvailability, + permission, + }) + updateOnOpen() + }} + icon={} + aria-label={'Edit ' + domain} + mr="2" + /> + + + )} + + ) +} +AdminDomainList.propTypes = { + nodes: array.isRequired, + verified: bool, + permission: string, + setSelectedRemoveProps: func.isRequired, + removeOnOpen: func.isRequired, + setModalProps: func.isRequired, + updateOnOpen: func.isRequired, +} diff --git a/frontend/src/admin/AdminDomainModal.js b/frontend/src/admin/AdminDomainModal.js index fb172abc1..38394509d 100644 --- a/frontend/src/admin/AdminDomainModal.js +++ b/frontend/src/admin/AdminDomainModal.js @@ -1,6 +1,5 @@ import React, { useRef } from 'react' -import { t, Trans } from '@lingui/macro' -import { useLingui } from '@lingui/react' +import { Trans, useLingui } from '@lingui/react/macro' import { Badge, Box, @@ -8,11 +7,7 @@ import { Divider, Flex, FormControl, - FormErrorMessage, FormLabel, - Grid, - IconButton, - Input, Modal, ModalBody, ModalCloseButton, @@ -31,37 +26,45 @@ import { Tooltip, useToast, } from '@chakra-ui/react' -import { AddIcon, MinusIcon, QuestionOutlineIcon, SmallAddIcon } from '@chakra-ui/icons' +import { AddIcon, QuestionOutlineIcon } from '@chakra-ui/icons' import { array, bool, func, number, object, string } from 'prop-types' -import { Field, FieldArray, Formik } from 'formik' +import { FieldArray, Formik } from 'formik' import { useMutation } from '@apollo/client' +import { ABTestVariant, ABTestWrapper } from '../app/ABTestWrapper' import { DomainField } from '../components/fields/DomainField' import { CREATE_DOMAIN, UPDATE_DOMAIN } from '../graphql/mutations' -import { ABTestVariant, ABTestWrapper } from '../app/ABTestWrapper' +import withSuperAdmin from '../app/withSuperAdmin' +import { CvdEnrollmentForm } from './CvdEnrollmentForm' -export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ...props }) { - const { - editingDomainId, - editingDomainUrl, - selectorInputList, - tagInputList, - orgSlug, - archived, - hidden, - permission, - mutation, - orgCount, - } = props +export function AdminDomainModal({ + isOpen, + onClose, + validationSchema, + orgId, + availableTags, + editingDomainId, + editingDomainUrl, + tagInputList, + orgSlug, + archived, + assetState, + mutation, + orgCount, + cvdEnrollment, + highAvailability, + permission, + ...rest +}) { const toast = useToast() const initialFocusRef = useRef() - const { i18n } = useLingui() + const { t } = useLingui() const [createDomain] = useMutation(CREATE_DOMAIN, { refetchQueries: ['PaginatedOrgDomains', 'FindAuditLogs'], onError(error) { toast({ - title: i18n._(t`An error occurred.`), + title: t`An error occurred.`, description: error.message, status: 'error', duration: 9000, @@ -73,8 +76,8 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... if (createDomain.result.__typename === 'Domain') { onClose() toast({ - title: i18n._(t`Domain added`), - description: i18n._(t`${createDomain.result.domain} was added to ${orgSlug}`), + title: t`Domain added`, + description: t`${createDomain.result.domain} was added to ${orgSlug}`, status: 'success', duration: 9000, isClosable: true, @@ -82,7 +85,7 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... }) } else if (createDomain.result.__typename === 'DomainError') { toast({ - title: i18n._(t`Unable to create new domain.`), + title: t`Unable to create new domain.`, description: createDomain.result.description, status: 'error', duration: 9000, @@ -91,8 +94,8 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... }) } else { toast({ - title: i18n._(t`Incorrect send method received.`), - description: i18n._(t`Incorrect createDomain.result typename.`), + title: t`Incorrect send method received.`, + description: t`Incorrect createDomain.result typename.`, status: 'error', duration: 9000, isClosable: true, @@ -107,7 +110,7 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... refetchQueries: ['FindAuditLogs'], onError(error) { toast({ - title: i18n._(t`An error occurred.`), + title: t`An error occurred.`, description: error.message, status: 'error', duration: 9000, @@ -119,10 +122,8 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... if (updateDomain.result.__typename === 'Domain') { onClose() toast({ - title: i18n._(t`Domain updated`), - description: i18n._( - t`${editingDomainUrl} from ${orgSlug} successfully updated to ${updateDomain.result.domain}`, - ), + title: t`Domain updated`, + description: t`${editingDomainUrl} from ${orgSlug} successfully updated.`, status: 'success', duration: 9000, isClosable: true, @@ -130,7 +131,7 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... }) } else if (updateDomain.result.__typename === 'DomainError') { toast({ - title: i18n._(t`Unable to update domain.`), + title: t`Unable to update domain.`, description: updateDomain.result.description, status: 'error', duration: 9000, @@ -139,8 +140,8 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... }) } else { toast({ - title: i18n._(t`Incorrect send method received.`), - description: i18n._(t`Incorrect updateDomain.result typename.`), + title: t`Incorrect send method received.`, + description: t`Incorrect updateDomain.result typename.`, status: 'error', duration: 9000, isClosable: true, @@ -149,38 +150,41 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... console.log('Incorrect updateDomain.result typename.') } }, - }) + update: (cache, { data }) => { + if (data.updateDomain.result.__typename !== 'Domain') return - const tagOptions = [ - { en: 'NEW', fr: 'NOUVEAU' }, - { en: 'PROD', fr: 'PROD' }, - { en: 'STAGING', fr: 'DEV' }, - { en: 'TEST', fr: 'TEST' }, - { en: 'WEB', fr: 'WEB' }, - { en: 'INACTIVE', fr: 'INACTIF' }, - { en: 'OUTSIDE', fr: 'EXTERIEUR' }, - ] + const updateDomainId = cache.identify(data.updateDomain.result) + cache.modify({ + id: updateDomainId, + fields: { + claimTags() { + return data.updateDomain.result.claimTags + }, + }, + }) + }, + }) const addableTags = (values, helper) => { - const stringValues = values?.map((label) => { - return label[i18n.locale] + const stringValues = values?.map(({ tagId }) => { + return tagId }) - const difference = tagOptions.filter((label) => !stringValues?.includes(label[i18n.locale])) - return difference?.map((label, idx) => { + const difference = availableTags.filter(({ tagId }) => !stringValues?.includes(tagId)) + return difference?.map((tag, idx) => { return ( ) @@ -189,29 +193,27 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... const getInitTags = () => { let tags = tagInputList?.map((label) => { - return tagOptions.filter((option) => { - return option[i18n.locale] == label - })[0] + return availableTags.filter((option) => option.tagId == label.tagId)[0] }) - if (mutation === 'create' && tags.filter((tag) => tag.en === 'NEW').length === 0) { - tags.push(tagOptions[0]) + if (mutation === 'create' && tags.filter(({ tagId }) => tagId === 'new-nouveau').length === 0) { + const newTag = availableTags.filter(({ tagId }) => tagId === 'new-nouveau')[0] + newTag && tags.push(newTag) } return tags } return ( - + { // Submit update detail mutation + const sanitizeCvdEnrollment = (enrollment) => { + if (!enrollment || typeof enrollment !== 'object') return enrollment + const { __typename, ...rest } = enrollment + return rest + } if (mutation === 'update') { await updateDomain({ variables: { domainId: editingDomainId, orgId: orgId, - domain: values.domainUrl.trim(), - selectors: values.selectors, - tags: values.tags, + tags: values.tags.map(({ tagId }) => tagId), archived: values.archiveDomain, - hidden: values.hideDomain, - outsideComment: values.outsideComment, + assetState: values.assetState, + ignoreRua: values.ignoreRua, + cvdEnrollment: sanitizeCvdEnrollment(values.cvdEnrollment), + highAvailability: values.highAvailability, }, }) } else if (mutation === 'create') { @@ -237,17 +244,16 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... variables: { orgId: orgId, domain: values.domainUrl.trim(), - selectors: values.selectors, - tags: values.tags, + tags: values.tags.map(({ tagId }) => tagId), archived: values.archiveDomain, - hidden: values.hideDomain, - outsideComment: values.outsideComment, + assetState: values.assetState, + cvdEnrollment: sanitizeCvdEnrollment(values.cvdEnrollment), }, }) } }} > - {({ handleSubmit, handleChange, isSubmitting, values, errors, touched }) => ( + {({ handleSubmit, handleChange, isSubmitting, values }) => (
{mutation === 'update' ? Edit Domain Details : Add Domain Details} @@ -255,83 +261,30 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... - - - - - ( - - - DKIM Selectors: - - {values.selectors.map((_selector, index) => ( - - - } - data-testid="remove-dkim-selector" - type="button" - p="3" - onClick={() => arrayHelpers.remove(index)} - aria-label="remove-dkim-selector" - /> - - {({ field }) => ( - - )} - - - - {errors && errors.selectors && errors.selectors[index]} - - - - ))} - } - data-testid="add-dkim-selector" - type="button" - px="2" - onClick={() => arrayHelpers.push('')} - aria-label="add-dkim-selector" - /> - - )} - /> - - + {mutation === 'create' ? ( + + ) : ( + + Domain: + {editingDomainUrl} + + )} ( Tags: - {values.tags?.map((label, idx) => { + {values.tags?.map(({ tagId, label, description }, idx) => { return ( - {label[i18n.locale]} + + {label.toUpperCase()} + arrayHelpers.remove(idx)} - aria-label={`remove-tag-${label[i18n.locale]}`} + aria-label={`remove-tag-${tagId}`} /> ) @@ -344,81 +297,54 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... )} /> - {values.tags?.find(({ en }) => en === 'OUTSIDE') && ( - - - Reason - - - - - Note: Domains from outside the GC scope may not be scanned right away - - - - )} - - - - - - - Hide domain - - - {permission === 'SUPER_ADMIN' && ( - - - - - - - - Archive domain - - + + + + + Asset State + + + + + - - {orgCount > 0 ? ( - Note: This will affect results for {orgCount} organizations - ) : ( - Note: This could affect results for multiple organizations - )} - - - )} + + + + + + + + + Please allow up to 24 hours for summaries to reflect any changes. @@ -438,6 +364,90 @@ export function AdminDomainModal({ isOpen, onClose, validationSchema, orgId, ... ) } +const ArchiveDomainSwitch = withSuperAdmin(({ defaultChecked, handleChange, orgCount }) => { + const { t } = useLingui() + + return ( + + + + + + + + Archive domain + + + + + {orgCount > 0 ? ( + Note: This will affect results for {orgCount} organizations + ) : ( + Note: This could affect results for multiple organizations + )} + + + ) +}) + +const HighAvailabilitySwitch = withSuperAdmin(({ defaultChecked, handleChange }) => { + const { t } = useLingui() + + return ( + + + + + + + + High Availability + + + + ) +}) + +const IgnoreRuaToggle = withSuperAdmin(({ defaultChecked, handleChange }) => { + return ( + + + + + Ignore RUA + + + + ) +}) + AdminDomainModal.propTypes = { isOpen: bool, onClose: func, @@ -445,14 +455,16 @@ AdminDomainModal.propTypes = { orgId: string, editingDomainId: string, editingDomainUrl: string, - selectorInputList: array, tagInputList: array, archived: bool, - hidden: bool, - permission: string, orgSlug: string, mutation: string, orgCount: number, refetchQueries: array, myOrg: object, + assetState: string, + cvdEnrollment: object, + highAvailability: bool, + availableTags: array, + permission: string, } diff --git a/frontend/src/admin/AdminDomains.js b/frontend/src/admin/AdminDomains.js index 27800efe7..c4d23133a 100644 --- a/frontend/src/admin/AdminDomains.js +++ b/frontend/src/admin/AdminDomains.js @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react' -import { t, Trans } from '@lingui/macro' +import { Trans, useLingui } from '@lingui/react/macro' import { Box, Button, @@ -7,7 +7,6 @@ import { Flex, FormControl, FormLabel, - IconButton, Input, InputGroup, InputLeftElement, @@ -20,22 +19,16 @@ import { ModalOverlay, Select, Stack, - Tag, - TagCloseButton, - TagLabel, Text, useDisclosure, useToast, } from '@chakra-ui/react' -import { AddIcon, EditIcon, MinusIcon, PlusSquareIcon } from '@chakra-ui/icons' +import { AddIcon, EditIcon, HamburgerIcon, PlusSquareIcon } from '@chakra-ui/icons' import { useMutation } from '@apollo/client' -import { useLingui } from '@lingui/react' -import { number, string } from 'prop-types' +import { array, bool, number, string } from 'prop-types' import { AdminDomainModal } from './AdminDomainModal' -import { AdminDomainCard } from './AdminDomainCard' -import { ListOf } from '../components/ListOf' import { LoadingMessage } from '../components/LoadingMessage' import { ErrorFallbackMessage } from '../components/ErrorFallbackMessage' import { RelayPaginationControls } from '../components/RelayPaginationControls' @@ -45,15 +38,20 @@ import { usePaginatedCollection } from '../utilities/usePaginatedCollection' import { PAGINATED_ORG_DOMAINS_ADMIN_PAGE as FORWARD } from '../graphql/queries' import { REMOVE_DOMAIN } from '../graphql/mutations' import { Formik } from 'formik' -import { SubdomainDiscoveryButton } from '../domains/SubdomainDiscoveryButton' -import { ABTestWrapper, ABTestVariant } from '../app/ABTestWrapper' +import { InfoBox, InfoButton, InfoPanel } from '../components/InfoPanel' +import { FilterList } from '../domains/FilterList' +import { domainSearchTip } from '../domains/DomainsPage' +import useSearchParam from '../utilities/useSearchParam' +import { DomainUpdateList } from './DomainUpdateList' +import { AdminDomainList } from './AdminDomainList' -export function AdminDomains({ orgSlug, orgId, permission }) { +export function AdminDomains({ orgSlug, orgId, verified, permission, availableTags }) { + const [showUpdateList, setShowUpdateList] = useState(false) const toast = useToast() - const { i18n } = useLingui() + const { t } = useLingui() const [newDomainUrl, setNewDomainUrl] = useState('') - const [domainsPerPage, setDomainsPerPage] = useState(20) + const [domainsPerPage, setDomainsPerPage] = useState(50) const [selectedRemoveProps, setSelectedRemoveProps] = useState({ domain: '', domainId: '', @@ -61,18 +59,23 @@ export function AdminDomains({ orgSlug, orgId, permission }) { }) const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('') const [modalProps, setModalProps] = useState({ - hidden: false, archived: false, mutation: '', tagInputList: [], - selectorInputList: [], + assetState: '', editingDomainId: '', editingDomainUrl: '', + cvdEnrollment: { status: 'NOT_ENROLLED' }, + highAvailability: false, + }) + const { searchValue: filters, setSearchParams: setFilters } = useSearchParam({ + name: 'domain-filters', + defaultValue: [], }) - const [filters, setFilters] = useState([]) const { isOpen: updateIsOpen, onOpen: updateOnOpen, onClose: updateOnClose } = useDisclosure() const { isOpen: removeIsOpen, onOpen: removeOnOpen, onClose: removeOnClose } = useDisclosure() + const { isOpen: infoIsOpen, onToggle } = useDisclosure() const validationSchema = schemaToValidation({ filterCategory: getRequirement('field'), @@ -87,15 +90,25 @@ export function AdminDomains({ orgSlug, orgId, permission }) { filters, } - const { loading, isLoadingMore, error, nodes, next, previous, resetToFirstPage, hasNextPage, hasPreviousPage } = - usePaginatedCollection({ - fetchForward: FORWARD, - recordsPerPage: domainsPerPage, - variables: fetchVariables, - relayRoot: 'findOrganizationBySlug.domains', - fetchPolicy: 'cache-and-network', - nextFetchPolicy: 'cache-first', - }) + const { + loading, + isLoadingMore, + error, + nodes, + next, + previous, + resetToFirstPage, + hasNextPage, + hasPreviousPage, + totalCount, + } = usePaginatedCollection({ + fetchForward: FORWARD, + recordsPerPage: domainsPerPage, + variables: fetchVariables, + relayRoot: 'findOrganizationBySlug.domains', + fetchPolicy: 'cache-and-network', + nextFetchPolicy: 'cache-first', + }) const memoizedSetDebouncedSearchTermCallback = useCallback(() => { setDebouncedSearchTerm(newDomainUrl) @@ -116,7 +129,7 @@ export function AdminDomains({ orgSlug, orgId, permission }) { }, onError(error) { toast({ - title: i18n._(t`An error occurred.`), + title: t`An error occurred.`, description: error.message, status: 'error', duration: 9000, @@ -128,8 +141,8 @@ export function AdminDomains({ orgSlug, orgId, permission }) { if (removeDomain.result.__typename === 'DomainResult') { removeOnClose() toast({ - title: i18n._(t`Domain removed`), - description: i18n._(t`Domain removed from ${orgSlug}`), + title: t`Domain removed`, + description: t`Domain removed from ${orgSlug}`, status: 'success', duration: 9000, isClosable: true, @@ -137,7 +150,7 @@ export function AdminDomains({ orgSlug, orgId, permission }) { }) } else if (removeDomain.result.__typename === 'DomainError') { toast({ - title: i18n._(t`Unable to remove domain.`), + title: t`Unable to remove domain.`, description: removeDomain.result.description, status: 'error', duration: 9000, @@ -146,8 +159,8 @@ export function AdminDomains({ orgSlug, orgId, permission }) { }) } else { toast({ - title: i18n._(t`Incorrect send method received.`), - description: i18n._(t`Incorrect removeDomain.result typename.`), + title: t`Incorrect send method received.`, + description: t`Incorrect removeDomain.result typename.`, status: 'error', duration: 9000, isClosable: true, @@ -161,19 +174,20 @@ export function AdminDomains({ orgSlug, orgId, permission }) { if (error) return const filterTagOptions = [ - { value: t`NEW`, text: t`New` }, - { value: t`PROD`, text: t`Prod` }, - { value: t`STAGING`, text: t`Staging` }, - { value: t`TEST`, text: t`Test` }, - { value: t`WEB`, text: t`Web` }, - { value: t`INACTIVE`, text: t`Inactive` }, - { value: t`OUTSIDE`, text: t`Outside` }, + ...availableTags?.map(({ tagId, label }) => ({ + value: tagId, + text: label.toUpperCase(), + })), { value: `NXDOMAIN`, text: `NXDOMAIN` }, { value: `BLOCKED`, text: t`Blocked` }, - { value: `WILDCARD_SIBLING`, text: t`Wildcard` }, + { value: `WILDCARD_SIBLING`, text: t`Wildcard Sibling` }, + { value: `WILDCARD_ENTRY`, text: t`Wildcard Entry` }, { value: `SCAN_PENDING`, text: t`Scan Pending` }, - { value: `HIDDEN`, text: t`Hidden` }, { value: `ARCHIVED`, text: t`Archived` }, + { value: `CVE_DETECTED`, text: t`SPIN Top 25` }, + { value: 'CVD_ENROLLED', text: t`CVD Enrolled` }, + { value: 'CVD_PENDING', text: t`CVD Pending` }, + { value: 'CVD_DENY', text: t`CVD Denied` }, ] const adminDomainList = loading ? ( @@ -185,7 +199,7 @@ export function AdminDomains({ orgSlug, orgId, permission }) { { - return [item['filterValue'], item] + if (item['filterCategory'] !== 'TAGS') return [item['filterCategory'], item] + else return [item['filterValue'], item] }), ).values(), ]) + resetToFirstPage() resetForm() }} > - {({ handleChange, handleSubmit, errors }) => { + {({ handleChange, handleSubmit, errors, values }) => { return ( Filters: - + + + + {errors.comparison} + + - {filterTagOptions.map(({ value, text }, idx) => { - return ( - + ) + }) + ) : ( + <> + + - ) - })} + + + + + )} {errors.filterValue} @@ -249,87 +308,65 @@ export function AdminDomains({ orgSlug, orgId, permission }) { ) }} - ( - - No Domains - - )} + + {showUpdateList ? ( + { + return { id, domain, tags: claimTags.map(({ label }) => label) } + })} + filters={filters} + search={debouncedSearchTerm} + domainCount={totalCount} + resetToFirstPage={resetToFirstPage} + /> + ) : ( + + )} + ) return ( - + { e.preventDefault() // prevents page from refreshing setModalProps({ - hidden: false, archived: false, mutation: 'create', tagInputList: [], - selectorInputList: [], editingDomainId: '', editingDomainUrl: newDomainUrl, orgCount: 0, @@ -353,22 +390,27 @@ export function AdminDomains({ orgSlug, orgId, permission }) { { setNewDomainUrl(e.target.value) resetToFirstPage() }} /> - + + {domainSearchTip} + - {filters.map(({ comparison, filterValue }, idx) => { - return ( - - {comparison === 'NOT_EQUAL' && !} - {filterValue} - setFilters(filters.filter((_, i) => i !== idx))} /> - - ) - })} + {adminDomainList} - + @@ -500,13 +530,39 @@ export function AdminDomains({ orgSlug, orgId, permission }) {
- + + + The "Asset State" describes how the domain relates to your organization. These states are used by Tracker to + give you a more accurate summary of your attack surface. + + + + + + + + + ) } AdminDomains.propTypes = { orgSlug: string.isRequired, orgId: string.isRequired, + verified: bool, + availableTags: array, domainsPerPage: number, permission: string, } diff --git a/frontend/src/admin/AdminPage.js b/frontend/src/admin/AdminPage.js index ad35aa6ef..80ceb91b2 100644 --- a/frontend/src/admin/AdminPage.js +++ b/frontend/src/admin/AdminPage.js @@ -1,10 +1,9 @@ import React, { useCallback, useState, useEffect } from 'react' import { Button, Flex, Stack, Text, useToast, Select } from '@chakra-ui/react' import { AddIcon } from '@chakra-ui/icons' -import { t, Trans } from '@lingui/macro' +import { Trans, useLingui } from '@lingui/react/macro' import { useQuery } from '@apollo/client' -import { Link as RouteLink, useHistory, useParams } from 'react-router-dom' -import { useLingui } from '@lingui/react' +import { Link as RouteLink, useNavigate, useParams } from 'react-router-dom' import { AdminPanel } from './AdminPanel' import { OrganizationInformation } from './OrganizationInformation' @@ -13,12 +12,14 @@ import { ADMIN_PAGE } from '../graphql/queries' import { Dropdown } from '../components/Dropdown' import { ErrorFallbackMessage } from '../components/ErrorFallbackMessage' import { useDebouncedFunction } from '../utilities/useDebouncedFunction' -import { bool } from 'prop-types' +import { bool, func, string } from 'prop-types' import { SuperAdminUserList } from './SuperAdminUserList' import { AuditLogTable } from './AuditLogTable' import { ErrorBoundary } from 'react-error-boundary' +import withSuperAdmin from '../app/withSuperAdmin' +import { DomainTagsList } from './DomainTagsList' -export default function AdminPage({ isLoginRequired }) { +export default function AdminPage() { const [selectedOrg, setSelectedOrg] = useState('none') const [orgDetails, setOrgDetails] = useState({}) const [searchTerm, setSearchTerm] = useState('') @@ -27,8 +28,8 @@ export default function AdminPage({ isLoginRequired }) { const { activeMenu } = useParams() const toast = useToast() - const history = useHistory() - const { i18n } = useLingui() + const navigate = useNavigate() + const { t } = useLingui() const memoizedSetDebouncedSearchTermCallback = useCallback(() => { setDebouncedSearchTerm(searchTerm) @@ -61,17 +62,19 @@ export default function AdminPage({ isLoginRequired }) { useEffect(() => { if (!activeMenu) { - history.replace(`/admin/organizations`) + navigate(`/admin/organizations`, { replace: true }) } if (initRender && data?.findMyOrganizations?.edges.length === 1) { setInitRender(false) setOrgDetails({ slug: data?.findMyOrganizations?.edges[0]?.node?.slug, id: data?.findMyOrganizations?.edges[0]?.node?.id, + verified: data?.findMyOrganizations?.edges[0]?.node?.verified, + availableTags: data?.findMyOrganizations?.edges[0]?.node?.availableTags || [], }) setSelectedOrg(data?.findMyOrganizations?.edges[0]?.node?.name || 'none') } - }, [activeMenu, history, data]) + }, [activeMenu, navigate, data]) if (error) { return @@ -83,10 +86,10 @@ export default function AdminPage({ isLoginRequired }) { if (loading) { dropdown = ( setSearchTerm(val)} searchValue={searchTerm} /> @@ -94,15 +97,16 @@ export default function AdminPage({ isLoginRequired }) { } else { options = [] data.findMyOrganizations?.edges.forEach((edge) => { - const { slug, name, id } = edge.node - options.push({ label: name, value: { slug: slug, id: id } }) + const { slug, name, id, verified, availableTags } = edge.node + options.push({ label: name, value: { slug: slug, id: id, verified: verified, availableTags } }) }) dropdown = ( setSearchTerm(val)} searchValue={searchTerm} onChange={(opt) => { @@ -114,9 +118,35 @@ export default function AdminPage({ isLoginRequired }) { ) } + if (!data?.isUserAdmin) { + return ( + + + You currently have no admin affiliations. + + + + Search for your organization to request an invite + + + + + + Is your organization not using Tracker yet? + + + + + ) + } + const changeActiveMenu = (val) => { if (activeMenu !== val) { - history.replace(`/admin/${val}`) + navigate(`/admin/${val}`, { replace: true }) } } @@ -125,6 +155,7 @@ export default function AdminPage({ isLoginRequired }) { {dropdown} + + + + + + About Coordinated Vulnerability Disclosure (CVD) + + + + + 1. What is Coordinated Vulnerability Disclosure (CVD)? +
A structured process that allows security researchers to report vulnerabilities safely and + responsibly. It ensures findings are received, validated, and addressed in an organized way, + helping organizations fix issues before they can be exploited. +
+
+ + + + 2. Why enroll your domains? +
+ Enrolling your internet‑facing assets ensures researchers can report real vulnerabilities directly + to the Government of Canada through an approved and safe channel. This improves early detection, + reduces security risk, and strengthens your organization’s ability to respond quickly and + consistently. +
+
+ + + + 3. Which domains should you enroll? +
+ Enroll any public, internet‑facing domains or subdomains your organization owns—especially + production systems that deliver services or expose application functionality. Test or pre‑launch + environments may be excluded unless they are publicly accessible. +
+
+
+ + + +
+ +
+ + + + + {values.cvdEnrollment.status !== 'NOT_ENROLLED' && ( + <> + + + + Description + + + + + + + + + Max Severity + + + + + + + + + Confidentiality Requirement + + + + + + + + + Integrity Requirement + + + + + + + + + Availability Requirement + + + + + + )} + + ) +} + +CvdEnrollmentForm.propTypes = { + values: object, + permission: string, + handleChange: func, +} diff --git a/frontend/src/admin/DomainTagsList.js b/frontend/src/admin/DomainTagsList.js new file mode 100644 index 000000000..28f9ec0f3 --- /dev/null +++ b/frontend/src/admin/DomainTagsList.js @@ -0,0 +1,176 @@ +import React, { useState } from 'react' +import { Badge, Box, Button, Collapse, Flex, IconButton, Switch, Text, useToast } from '@chakra-ui/react' +import { DOMAIN_TAGS } from '../graphql/queries' +import { useQuery } from '@apollo/client' +import { EditIcon, PlusSquareIcon, ViewIcon, ViewOffIcon } from '@chakra-ui/icons' +import { LoadingMessage } from '../components/LoadingMessage' +import { ErrorFallbackMessage } from '../components/ErrorFallbackMessage' +import { TagForm } from './TagForm' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" +import { string } from 'prop-types' + +export const DomainTagsList = ({ orgId, createOwnership }) => { + const [tagFormState, setTagFormState] = useState({ editingTags: {}, isCreatingTag: false }) + const [onlyVisible, setOnlyVisible] = useState(true) + const toast = useToast() + + const { loading, error, data } = useQuery(DOMAIN_TAGS, { + variables: { orgId, isVisible: onlyVisible }, + errorPolicy: 'ignore', + onError: (error) => { + const [_, message] = error.message.split(': ') + toast({ + title: 'Error', + description: message, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + }, + }) + + if (loading) return + if (error) return + + const ownershipBadgeColour = (ownership) => { + switch (ownership) { + case 'GLOBAL': + return 'weak' + case 'ORG': + return 'info' + case 'PENDING': + return 'strong' + default: + return 'primary' + } + } + + const ownershipLabel = { + GLOBAL: t`Global`, + ORG: t`Organization`, + PENDING: t`Pending`, + } + + let tagList + if (data.findAllTags.length === 0) { + tagList = ( + + No Tags + + ) + } else { + tagList = data.findAllTags.map(({ tagId, label, description, isVisible, ownership, organizations }) => { + return ( + + + } + variant="primary" + onClick={() => + setTagFormState((prev) => ({ + ...prev, + editingTags: { ...prev.editingTags, [tagId]: !prev.editingTags[tagId] }, + })) + } + mr="2" + /> + + + + {label.toUpperCase()} + + {organizations && ownership !== 'GLOBAL' && ( + + ({organizations.map(({ acronym }) => acronym).join(', ')}) + + )} + {!isVisible && } + + + {description} + + + {ownershipLabel[ownership]} + + + + + + + + ) + }) + } + + return ( + + + + + + + { + setOnlyVisible(e.target.checked) + }} + /> + + + + + + + + + {tagList} + + ) +} + +DomainTagsList.propTypes = { + orgId: string, + createOwnership: string, +} diff --git a/frontend/src/admin/DomainUpdateList.js b/frontend/src/admin/DomainUpdateList.js new file mode 100644 index 000000000..0c42f1eb2 --- /dev/null +++ b/frontend/src/admin/DomainUpdateList.js @@ -0,0 +1,385 @@ +import React, { useState, useRef } from 'react' +import { + Table, + Thead, + Tr, + Th, + Td, + Checkbox, + Box, + Button, + useDisclosure, + Drawer, + DrawerOverlay, + DrawerContent, + DrawerHeader, + DrawerBody, + DrawerFooter, + VisuallyHidden, + CheckboxGroup, + Tbody, + Text, + Flex, + useToast, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, +} from '@chakra-ui/react' +import { useMutation } from '@apollo/client' +import { array, func, number, string } from 'prop-types' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" +import { UPDATE_DOMAINS_BY_DOMAIN_IDS, UPDATE_DOMAINS_BY_FILTERS } from '../graphql/mutations' + +export function DomainUpdateList({ orgId, domains, availableTags, filters, search, domainCount, resetToFirstPage }) { + const toast = useToast() + // selectedIds is global across all pages + const [selectedIds, setSelectedIds] = useState(new Set()) + // selectAllGlobal means all filtered domains (not just visible) are selected + const [selectAllGlobal, setSelectAllGlobal] = useState(false) + const [tags, setTags] = useState([]) + const { isOpen, onOpen, onClose } = useDisclosure() + const { isOpen: isConfirmOpen, onOpen: onConfirmOpen, onClose: onConfirmClose } = useDisclosure() + + const resetSelections = () => { + setSelectedIds(new Set()) + setSelectAllGlobal(false) + setTags([]) + } + + const [updateDomainsByDomainIds, { loading: idLoading }] = useMutation(UPDATE_DOMAINS_BY_DOMAIN_IDS, { + refetchQueries: ['PaginatedOrgDomains', 'FindAuditLogs'], + onError(error) { + toast({ + title: t`An error occurred.`, + description: error.message, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + }, + onCompleted({ updateDomainsByDomainIds }) { + if (updateDomainsByDomainIds.result.__typename === 'DomainBulkResult') { + onClose() + resetSelections() + resetToFirstPage() + toast({ + title: t`Domains updated.`, + description: updateDomainsByDomainIds.result.status, + status: 'success', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else if (updateDomainsByDomainIds.result.__typename === 'DomainError') { + toast({ + title: t`Unable to update domains.`, + description: updateDomainsByDomainIds.result.description, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else { + toast({ + title: t`Incorrect send method received.`, + description: t`Incorrect updateDomainsByDomainIds.result typename.`, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + console.log('Incorrect updateDomainsByDomainIds.result typename.') + } + }, + }) + const [updateDomainsByFilters, { loading: filterLoading }] = useMutation(UPDATE_DOMAINS_BY_FILTERS, { + refetchQueries: ['PaginatedOrgDomains', 'FindAuditLogs'], + onError(error) { + toast({ + title: t`An error occurred.`, + description: error.message, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + }, + onCompleted({ updateDomainsByFilters }) { + if (updateDomainsByFilters.result.__typename === 'DomainBulkResult') { + onClose() + resetSelections() + resetToFirstPage() + toast({ + title: t`Domains updated.`, + description: updateDomainsByFilters.result.status, + status: 'success', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else if (updateDomainsByFilters.result.__typename === 'DomainError') { + toast({ + title: t`Unable to update domains.`, + description: updateDomainsByFilters.result.description, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else { + toast({ + title: t`Incorrect send method received.`, + description: t`Incorrect updateDomainsByDomainIds.result typename.`, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + console.log('Incorrect updateDomainsByFilters.result typename.') + } + }, + }) + + const liveRegionRef = useRef(null) + + // selection handlers + // Helper: get IDs of domains on current page + const currentPageIds = domains.map((d) => d.id) + // Helper: how many on this page are selected? + const selectedOnPage = domains.filter((d) => selectedIds.has(d.id)).length + // Helper: are all on this page selected? + const allOnPageSelected = domains.length > 0 && selectedOnPage === domains.length + // Helper: is some but not all on this page selected? + const someOnPageSelected = selectedOnPage > 0 && !allOnPageSelected + + const toggleDomain = (id) => { + let newSet = new Set(selectedIds) + if (newSet.has(id)) { + newSet.delete(id) + } else { + newSet.add(id) + } + setSelectedIds(newSet) + setSelectAllGlobal(false) + + if (liveRegionRef.current) { + if (newSet.size === 0) { + liveRegionRef.current.textContent = 'Selection cleared.' + } else if (allOnPageSelected) { + liveRegionRef.current.textContent = `All ${domains.length} domains on this page are selected.` + } else { + liveRegionRef.current.textContent = `${selectedOnPage} selected on this page.` + } + } + } + + const handleSelectAllPage = () => { + if (allOnPageSelected) { + // Deselect all on this page only + const newSet = new Set(selectedIds) + currentPageIds.forEach((id) => newSet.delete(id)) + setSelectedIds(newSet) + setSelectAllGlobal(false) + if (liveRegionRef.current) { + liveRegionRef.current.textContent = 'Selection cleared.' + } + } else { + // Add all on this page to selection (preserve others) + const newSet = new Set(selectedIds) + currentPageIds.forEach((id) => newSet.add(id)) + setSelectedIds(newSet) + setSelectAllGlobal(false) + if (liveRegionRef.current) { + liveRegionRef.current.textContent = `All ${domains.length} domains on this page are selected.` + } + } + } + + const handleSelectAllGlobal = () => { + setSelectAllGlobal(true) + // Add all visible to selectedIds (for UI feedback) + const newSet = new Set(selectedIds) + currentPageIds.forEach((id) => newSet.add(id)) + setSelectedIds(newSet) + if (liveRegionRef.current) { + liveRegionRef.current.textContent = `All ${domainCount} domains are selected.` + } + } + + const handleConfirmSubmit = async () => { + if (selectAllGlobal) { + await updateDomainsByFilters({ + variables: { filters, search, tags, orgId }, + }) + } else { + await updateDomainsByDomainIds({ + variables: { domainIds: Array.from(selectedIds), tags, orgId }, + }) + } + onConfirmClose() + } + + // Render all rows + const rows = domains.map((d) => { + const isChecked = selectedIds.has(d.id) + return ( + + + toggleDomain(d.id)} /> + + {d.domain} + {d.tags.join(', ').toUpperCase()} + + ) + }) + + return ( + + + + + + + + + + {rows} +
+ + + + + + Domain + + Current Tags +
+ + {/* Selection banner */} + {selectedOnPage > 0 && !selectAllGlobal && ( + + + + {allOnPageSelected ? ( + All {domains.length} domains on this page are selected. + ) : ( + {selectedOnPage} selected on this page. + )} + + {domainCount > domains.length && !selectAllGlobal && ( + + )} + + + )} + {selectAllGlobal && ( + + + + All {domainCount} domains are selected. Updates will apply to all filtered domains, not just this page. + + + + )} + + {/* Show number selected (global) */} + {selectedIds.size > 0 && !selectAllGlobal && ( + + {selectedIds.size} selected in total + + )} + {selectAllGlobal && ( + + All {domainCount} selected + + )} + + + + + + + + Apply Tags + + + setTags(values)}> + {availableTags.map(({ label, tagId }) => { + return ( + + + {label.toUpperCase()} + + + ) + })} + + + + + + + + + {/* Confirmation Modal */} + + + + + Are you sure? + + + + This will update {selectAllGlobal ? domainCount : selectedIds.size} domain(s). + + + Are you sure you want to apply these tag changes? + + + + + + + + + + +
+
+
+ ) +} + +DomainUpdateList.propTypes = { + domains: array, + filters: array, + availableTags: array, + orgId: string, + search: string, + domainCount: number, + resetToFirstPage: func, +} diff --git a/frontend/src/admin/OrganizationInformation.js b/frontend/src/admin/OrganizationInformation.js index 3516ec511..bd21591f7 100644 --- a/frontend/src/admin/OrganizationInformation.js +++ b/frontend/src/admin/OrganizationInformation.js @@ -4,6 +4,7 @@ import { Button, Collapse, Flex, + FormLabel, Grid, Heading, IconButton, @@ -15,6 +16,7 @@ import { ModalHeader, ModalOverlay, Stack, + Switch, Text, useDisclosure, useToast, @@ -22,7 +24,8 @@ import { import { CheckCircleIcon, MinusIcon, EditIcon } from '@chakra-ui/icons' import { bool, func, string } from 'prop-types' import { useMutation, useQuery } from '@apollo/client' -import { t, Trans } from '@lingui/macro' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" import { Formik } from 'formik' import { ORGANIZATION_INFORMATION } from '../graphql/queries' @@ -31,8 +34,9 @@ import { FormField } from '../components/fields/FormField' import { LoadingMessage } from '../components/LoadingMessage' import { ErrorFallbackMessage } from '../components/ErrorFallbackMessage' import { getRequirement, schemaToValidation } from '../utilities/fieldRequirements' +import withSuperAdmin from '../app/withSuperAdmin' -export function OrganizationInformation({ orgSlug, removeOrgCallback: setSelectedOrg, isUserSuperAdmin, ...props }) { +export function OrganizationInformation({ orgSlug, removeOrgCallback: setSelectedOrg, ...props }) { const toast = useToast() const { isOpen: isRemovalOpen, onOpen: onRemovalOpen, onClose: onRemovalClose } = useDisclosure() const removeOrgBtnRef = useRef() @@ -238,20 +242,28 @@ export function OrganizationInformation({ orgSlug, removeOrgCallback: setSelecte provinceFR: '', cityEN: '', cityFR: '', + externalId: '', + psd: org.policies?.psd ?? false, + pgs: org.policies?.pgs ?? false, }} validationSchema={updateOrgValidationSchema} onSubmit={async (values, formikHelpers) => { // Update the organization (only include fields that have values) const propertiesWithValues = {} + const { psd, pgs, ...stringValues } = values + // Extract only the entries that have truthy values - Object.entries(values).forEach((entry) => { + Object.entries(stringValues).forEach((entry) => { const [key, value] = entry if (value) { propertiesWithValues[key] = value } }) + if (psd !== (org.policies?.psd ?? false)) propertiesWithValues.psd = psd + if (pgs !== (org.policies?.pgs ?? false)) propertiesWithValues.pgs = pgs + // Handle case where user does not supply any fields to update if (Object.keys(propertiesWithValues).length === 0) { toast({ @@ -280,7 +292,7 @@ export function OrganizationInformation({ orgSlug, removeOrgCallback: setSelecte } }} > - {({ handleSubmit, handleReset }) => ( + {({ handleSubmit, handleReset, values, setFieldValue }) => (
Blank fields will not be included when updating the organization. - {isUserSuperAdmin && ( - <> - - - - - - - - )} + @@ -326,6 +329,8 @@ export function OrganizationInformation({ orgSlug, removeOrgCallback: setSelecte + + - - {userAffiliations} - - - - { - await closeAccount({ - variables: { userId: editUserRole.userId }, - }) + + Affiliations: {totalCount} + + + + - - - - - - )} - - + Close Account + +
+ {userAffiliations} + ) }) @@ -434,6 +362,7 @@ export function SuperAdminUserList({ permission }) { resetToFirstPage={resetToFirstPage} orderByOptions={orderByOptions} placeholder={t`Search for a user (email)`} + totalRecords={totalCount} /> {userList} + + { + await closeAccount({ + variables: { userId: editUserRole.userId }, + }) + }} + > + {({ handleSubmit }) => ( +
+ + + + Close Account + + + + + This action CANNOT be reversed, are you sure you wish to to close the account{' '} + {editUserRole.displayName}? + + + + + Enter "{editUserRole.userName}" below to confirm removal. This field is case-sensitive. + + + + + + + + + + + + + + )} +
+
) } - -SuperAdminUserList.propTypes = { - permission: string, -} diff --git a/frontend/src/admin/TagForm.js b/frontend/src/admin/TagForm.js new file mode 100644 index 000000000..41603dc55 --- /dev/null +++ b/frontend/src/admin/TagForm.js @@ -0,0 +1,289 @@ +import React from 'react' +import { Badge, Box, Button, Flex, FormLabel, Grid, Select, Switch, Text, useToast } from '@chakra-ui/react' +import { useMutation } from '@apollo/client' +import { CREATE_TAG, UPDATE_TAG } from '../graphql/mutations' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" +import { Formik } from 'formik' +import { FormField } from '../components/fields/FormField' +import { getRequirement, schemaToValidation } from '../utilities/fieldRequirements' +import { bool, string, func } from 'prop-types' +import withSuperAdmin from '../app/withSuperAdmin' + +export function TagForm({ mutation, tagId = '', visible = true, ownership, setTagFormState, orgId }) { + const toast = useToast() + + const fieldRequirement = getRequirement('field') + const validationSchema = schemaToValidation({ + labelEn: fieldRequirement, + labelFr: fieldRequirement, + isVisible: fieldRequirement, + ownership: fieldRequirement, + }) + + const [updateTag, { loading: updateLoading }] = useMutation(UPDATE_TAG, { + refetchQueries: ['FindAllTags'], + onError(error) { + toast({ + title: t`An error occurred.`, + description: error.message, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + }, + onCompleted({ updateTag }) { + if (updateTag.result.__typename === 'Tag') { + toast({ + title: t`Tag updated`, + description: t`${updateTag.result.tagId} was successfully updated.`, + status: 'success', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else if (updateTag.result.__typename === 'TagError') { + toast({ + title: t`Unable to update tag.`, + description: updateTag.result.description, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else { + toast({ + title: t`Incorrect send method received.`, + description: `Incorrect updateTag.result typename.`, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + console.log('Incorrect updateTag.result typename.') + } + }, + }) + + const [createTag, { loading: createLoading }] = useMutation(CREATE_TAG, { + refetchQueries: ['FindAllTags'], + onError(error) { + toast({ + title: t`An error occurred.`, + description: error.message, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + }, + onCompleted({ createTag }) { + if (createTag.result.__typename === 'Tag') { + toast({ + title: t`Tag created`, + description: t`${createTag.result.tagId} was added to tag list.`, + status: 'success', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + setTagFormState((prev) => ({ ...prev, isCreatingTag: false })) + } else if (createTag.result.__typename === 'TagError') { + toast({ + title: t`Unable to create new tag.`, + description: createTag.result.description, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else { + toast({ + title: t`Incorrect send method received.`, + description: `Incorrect createTag.result typename.`, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + console.log('Incorrect createTag.result typename.') + } + }, + }) + + const isLoading = createLoading || updateLoading + + const initial = { + labelEn: '', + labelFr: '', + descriptionEn: '', + descriptionFr: '', + isVisible: visible, + ownership, + orgId, + } + + return ( + + + + Note: Tags must not include data that would reclassify the asset above{' '} + Unclassified, Low Integrity, Low Availability (ULL) + + + { + if (mutation === 'create') { + await createTag({ variables: values }) + } else if (mutation === 'update') { + // Only include fields that have changed from initial values + const propertiesWithValues = {} + Object.entries(values).forEach(([key, value]) => { + // Only include if value is different from initial + if (value !== initial[key] && value !== '' && value !== undefined) { + propertiesWithValues[key] = value + } + }) + + // Handle case where user does not supply any fields to update + if (Object.keys(propertiesWithValues).length === 0) { + toast({ + title: t`Tag not updated`, + description: t`No values were supplied when attempting to update organization details.`, + status: 'warning', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + + return + } + + const updateResponse = await updateTag({ + variables: { + tagId, + orgId, + ...propertiesWithValues, + }, + }) + // Close and reset form if successfully updated organization + if (updateResponse.data.updateTag.result.__typename === 'Tag') { + setTagFormState((prev) => ({ + ...prev, + editingTags: { ...prev.editingTags, [tagId]: false }, + })) + formikHelpers.resetForm() + } + } + }} + > + {({ handleSubmit, handleReset, handleChange }) => ( +
+ + + + + + + + + + + + + + + + + + Visible + + + + + + + + +
+ )} +
+
+ ) +} + +const OwnershipSelect = withSuperAdmin(({ ownership, handleChange, orgId }) => { + return ( + + + + Ownership: + + + + + ) +}) + +TagForm.propTypes = { + mutation: string, + tagId: string, + visible: bool, + ownership: string, + setTagFormState: func, + orgId: string, +} diff --git a/frontend/src/admin/UserList.js b/frontend/src/admin/UserList.js index 65586c43c..f8230ddcb 100644 --- a/frontend/src/admin/UserList.js +++ b/frontend/src/admin/UserList.js @@ -8,12 +8,12 @@ import { Input, InputGroup, InputLeftElement, - Stack, Text, useDisclosure, } from '@chakra-ui/react' import { AddIcon, EditIcon, EmailIcon, MinusIcon } from '@chakra-ui/icons' -import { t, Trans } from '@lingui/macro' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" import { string } from 'prop-types' import { UserListModal } from './UserListModal' @@ -34,7 +34,7 @@ export function UserList({ includePending, permission, orgSlug, orgId }) { id: null, userName: null, }) - const [usersPerPage, setUsersPerPage] = useState(20) + const [usersPerPage, setUsersPerPage] = useState(50) const [editingUserRole, setEditingUserRole] = useState() const [editingUserName, setEditingUserName] = useState() const { isOpen, onOpen, onClose } = useDisclosure() @@ -47,15 +47,30 @@ export function UserList({ includePending, permission, orgSlug, orgId }) { useDebouncedFunction(memoizedSetDebouncedSearchTermCallback, 500) - const { loading, isLoadingMore, error, nodes, next, previous, hasNextPage, hasPreviousPage, resetToFirstPage } = - usePaginatedCollection({ - fetchForward: FORWARD, - recordsPerPage: usersPerPage, - variables: { orgSlug, search: debouncedSearchUser, includePending }, - relayRoot: 'findOrganizationBySlug.affiliations', - fetchPolicy: 'cache-and-network', - nextFetchPolicy: 'cache-first', - }) + const { + loading, + isLoadingMore, + error, + nodes, + next, + previous, + hasNextPage, + hasPreviousPage, + resetToFirstPage, + totalCount, + } = usePaginatedCollection({ + fetchForward: FORWARD, + recordsPerPage: usersPerPage, + variables: { + orgSlug, + search: debouncedSearchUser, + includePending, + orderBy: { field: 'PERMISSION', direction: 'ASC' }, + }, + relayRoot: 'findOrganizationBySlug.affiliations', + fetchPolicy: 'cache-and-network', + nextFetchPolicy: 'cache-first', + }) if (error) return @@ -68,48 +83,42 @@ export function UserList({ includePending, permission, orgSlug, orgId }) { No users ) : ( - nodes.map((node) => { - const userRole = node.permission + nodes.map(({ id, permission: userRole, user }) => { return ( - - - - { - setSelectedRemoveUser(node.user) - setMutation('remove') - onOpen() - }} - p={2} - m={0} - icon={} - /> - { - setEditingUserRole(userRole) - setEditingUserName(node.user.userName) - setMutation('update') - onOpen() - }} - p={2} - m={0} - icon={} - /> - - - - - + + { + setSelectedRemoveUser(user) + setMutation('remove') + onOpen() + }} + px="2" + mr="1" + icon={} + /> + { + setEditingUserRole(userRole) + setEditingUserName(user.userName) + setMutation('update') + onOpen() + }} + px="2" + mr="2" + icon={} + /> + ) }) ) @@ -143,6 +152,7 @@ export function UserList({ includePending, permission, orgSlug, orgId }) { @@ -183,6 +194,7 @@ export function UserList({ includePending, permission, orgSlug, orgId }) { next={next} previous={previous} isLoadingMore={isLoadingMore} + totalRecords={totalCount} /> ', () => { it('represents a domain', async () => { const { getByText } = render( - - - - - - - - - + + + + + + + + + + + , ) diff --git a/frontend/src/admin/__tests__/AdminDomains.test.js b/frontend/src/admin/__tests__/AdminDomains.test.js index bde756788..f3de2b82d 100644 --- a/frontend/src/admin/__tests__/AdminDomains.test.js +++ b/frontend/src/admin/__tests__/AdminDomains.test.js @@ -3,36 +3,23 @@ import { fireEvent, render, waitFor } from '@testing-library/react' import { theme, ChakraProvider } from '@chakra-ui/react' import { MemoryRouter } from 'react-router-dom' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { MockedProvider } from '@apollo/client/testing' import { makeVar } from '@apollo/client' import userEvent from '@testing-library/user-event' -import { en } from 'make-plural/plurals' - import { AdminDomains } from '../AdminDomains' - import { createCache } from '../../client' import { UserVarProvider } from '../../utilities/userState' import { rawOrgDomainListData, rawOrgDomainListDataEmpty } from '../../fixtures/orgDomainListData' import { PAGINATED_ORG_DOMAINS_ADMIN_PAGE as FORWARD } from '../../graphql/queries' import { CREATE_DOMAIN, REMOVE_DOMAIN, UPDATE_DOMAIN } from '../../graphql/mutations' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) - const mocks = [ { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -55,6 +42,7 @@ describe('', () => { orgId={rawOrgDomainListData.findOrganizationBySlug.id} orgSlug="test-org.slug" domainsPerPage={4} + availableTags={rawOrgDomainListData.findOrganizationBySlug.availableTags} /> @@ -75,7 +63,7 @@ describe('', () => { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -96,6 +84,7 @@ describe('', () => { orgId={rawOrgDomainListData.findOrganizationBySlug.id} orgSlug={'test-org.slug'} domainsPerPage={4} + availableTags={rawOrgDomainListData.findOrganizationBySlug.availableTags} /> @@ -120,7 +109,7 @@ describe('', () => { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -135,11 +124,8 @@ describe('', () => { variables: { orgId: 'gwdsfgvwsdgfvswefgdv', domain: 'test-domain.gc.ca', - selectors: [], - tags: [{ en: 'NEW', fr: 'NOUVEAU' }], - hidden: false, + tags: [], archived: false, - outsideComment: null, }, }, result: { @@ -172,6 +158,7 @@ describe('', () => { orgId={rawOrgDomainListData.findOrganizationBySlug.id} orgSlug={'test-org.slug'} domainsPerPage={4} + availableTags={rawOrgDomainListData.findOrganizationBySlug.availableTags} /> @@ -200,7 +187,7 @@ describe('', () => { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -213,7 +200,7 @@ describe('', () => { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -228,11 +215,10 @@ describe('', () => { variables: { orgId: 'testid=', domain: 'test-domain.gc.ca', - selectors: [], - tags: [{ en: 'NEW', fr: 'NOUVEAU' }], - hidden: false, + tags: [], archived: false, - outsideComment: null, + assetState: 'APPROVED', + cvdEnrollment: { status: 'NOT_ENROLLED' }, }, }, result: { @@ -266,6 +252,7 @@ describe('', () => { orgId={rawOrgDomainListData.findOrganizationBySlug.id} orgSlug={'test-org.slug'} domainsPerPage={4} + availableTags={rawOrgDomainListData.findOrganizationBySlug.availableTags} /> @@ -297,7 +284,7 @@ describe('', () => { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -310,7 +297,7 @@ describe('', () => { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -325,11 +312,10 @@ describe('', () => { variables: { orgId: rawOrgDomainListData.findOrganizationBySlug.id, domain: 'test.domain.gc.ca', - selectors: [], - tags: [{ en: 'NEW', fr: 'NOUVEAU' }], - hidden: false, + tags: [], archived: false, - outsideComment: null, + assetState: 'APPROVED', + cvdEnrollment: { status: 'NOT_ENROLLED' }, }, }, result: { @@ -346,13 +332,14 @@ describe('', () => { }, ] - const { getByText, getByPlaceholderText, findByText, queryByText } = render( + const { getByText, getByPlaceholderText, findByText, queryByText, findByRole } = render( @@ -362,6 +349,7 @@ describe('', () => { orgId={rawOrgDomainListData.findOrganizationBySlug.id} orgSlug={'test-org.slug'} domainsPerPage={4} + availableTags={rawOrgDomainListData.findOrganizationBySlug.availableTags} /> @@ -381,118 +369,22 @@ describe('', () => { userEvent.click(addDomainButton) - const confirmBtn = getByText(/Confirm/) - fireEvent.click(confirmBtn) + await waitFor(() => expect(getByText(/Add Domain Details/i)).toBeInTheDocument()) - await waitFor(() => expect(getByText(/Domain added/i)).toBeInTheDocument()) + const assetStateSelect = await findByRole('combobox', { name: /Asset State/ }) - await waitFor(() => expect(queryByText('Add Domain Details')).not.toBeInTheDocument()) - }) - - it.skip('succeeds when DKIM selectors are added', async () => { - const mocks = [ - { - request: { - query: FORWARD, - variables: { - first: 20, - orgSlug: 'test-org.slug', - search: '', - orderBy: { field: 'DOMAIN', direction: 'ASC' }, - filters: [], - }, - }, - result: { data: rawOrgDomainListData }, - }, - { - request: { - query: CREATE_DOMAIN, - variables: { - orgId: rawOrgDomainListData.findOrganizationBySlug.id, - domain: 'test.domain.gc.ca', - selectors: ['selector1'], - tags: [{ en: 'NEW', fr: 'NOUVEAU' }], - hidden: false, - archived: false, - outsideComment: null, - }, - }, - result: { - data: { - createDomain: { - result: { - domain: 'lauretta.name', - __typename: 'Domain', - }, - __typename: 'CreateDomainPayload', - }, - }, - }, + fireEvent.change(assetStateSelect, { + target: { + value: 'APPROVED', }, - ] - - const { getByText, getByTestId, getByPlaceholderText, queryAllByText, findByText, getByRole } = render( - - - - - - - - - - - , - ) - - const addDomainBtn = await findByText(/Add Domain/) - userEvent.click(addDomainBtn) - - await waitFor(() => expect(getByText(/Add Domain Details/)).toBeInTheDocument()) - - const domainInput = getByRole('textbox', { name: /New Domain URL/ }) - expect(domainInput).toBeInTheDocument() - userEvent.type(domainInput, 'test.domain.gc.ca') - - const addSelectorBtn = getByTestId(/add-dkim-selector/) - fireEvent.click(addSelectorBtn) - - const selectorInput = getByPlaceholderText(/DKIM Selector/) - fireEvent.blur(selectorInput) - - await waitFor(() => expect(getByText(/Selector cannot be empty/)).toBeInTheDocument()) - - fireEvent.change(selectorInput, { target: { value: 'selector1.' } }) - - // await waitFor(() => - // expect( - // getByText( - // /Selector must be string containing alphanumeric characters and periods, starting and ending with only alphanumeric characters/, - // ), - // ).toBeInTheDocument(), - // ) - - fireEvent.change(selectorInput, { - target: { value: 'selector1' }, }) const confirmBtn = getByText(/Confirm/) fireEvent.click(confirmBtn) - await waitFor(() => { - const successMessages = queryAllByText(/Domain added/i) - expect(successMessages[0]).toBeVisible() - }) + await waitFor(() => expect(getByText(/Domain added/i)).toBeInTheDocument()) + + await waitFor(() => expect(queryByText('Add Domain Details')).not.toBeInTheDocument()) }) }) @@ -503,7 +395,7 @@ describe('', () => { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -551,6 +443,7 @@ describe('', () => { orgId={rawOrgDomainListData.findOrganizationBySlug.id} orgSlug={'test-org.slug'} domainsPerPage={4} + availableTags={rawOrgDomainListData.findOrganizationBySlug.availableTags} /> @@ -575,13 +468,13 @@ describe('', () => { }) describe('editing a domain', () => { - it.skip('successfully edits domain URL', async () => { + it('successfully edits domain details', async () => { const mocks = [ { request: { query: FORWARD, variables: { - first: 20, + first: 50, orgSlug: 'test-org.slug', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -596,12 +489,12 @@ describe('', () => { variables: { domainId: 'testid2=', orgId: 'testid=', - domain: 'test.domain.ca', - selectors: [], tags: [], - hidden: false, archived: false, - outsideComment: null, + assetState: 'MONITOR_ONLY', + ignoreRua: undefined, + cvdEnrollment: { status: 'NOT_ENROLLED' }, + highAvailability: false, }, }, result: { @@ -618,13 +511,14 @@ describe('', () => { }, ] - const { getByText, findByTestId, getByLabelText, queryByText } = render( + const { getByText, findByTestId, queryByText, findByRole } = render( @@ -634,6 +528,7 @@ describe('', () => { orgId={rawOrgDomainListData.findOrganizationBySlug.id} orgSlug={'test-org.slug'} domainsPerPage={4} + availableTags={rawOrgDomainListData.findOrganizationBySlug.availableTags} /> @@ -647,10 +542,11 @@ describe('', () => { await waitFor(() => expect(getByText(/Edit Domain Details/i)).toBeInTheDocument()) - const editDomainInput = getByLabelText(/Domain URL:/) - fireEvent.change(editDomainInput, { + const assetStateSelect = await findByRole('combobox', { name: /Asset State/ }) + + fireEvent.change(assetStateSelect, { target: { - value: 'test.domain.ca', + value: 'MONITOR_ONLY', }, }) diff --git a/frontend/src/admin/__tests__/AdminPage.test.js b/frontend/src/admin/__tests__/AdminPage.test.js index 503e31fee..50ec096d9 100644 --- a/frontend/src/admin/__tests__/AdminPage.test.js +++ b/frontend/src/admin/__tests__/AdminPage.test.js @@ -1,16 +1,16 @@ import React from 'react' import { theme, ChakraProvider } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { MockedProvider } from '@apollo/client/testing' import AdminPage from '../AdminPage' import { waitFor, render } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' import userEvent from '@testing-library/user-event' - import { UserVarProvider } from '../../utilities/userState' +import { TourProvider } from '../../userOnboarding/contexts/TourContext' + import { ADMIN_PAGE, ORGANIZATION_INFORMATION, @@ -18,16 +18,6 @@ import { PAGINATED_ORG_DOMAINS_ADMIN_PAGE, } from '../../graphql/queries' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) - describe('', () => { it('shows a list of the users organizations', async () => { const { getByText } = render( @@ -36,7 +26,9 @@ describe('', () => { - + + + @@ -63,7 +55,9 @@ describe('', () => { - + + + @@ -100,7 +94,9 @@ describe('', () => { - + + + @@ -175,6 +171,7 @@ function mocks() { ], }, isUserSuperAdmin: true, + isUserAdmin: true, }, }, }, @@ -207,6 +204,7 @@ function mocks() { ], }, isUserSuperAdmin: true, + isUserAdmin: true, }, }, }, @@ -237,7 +235,7 @@ function mocks() { request: { query: PAGINATED_ORG_DOMAINS_ADMIN_PAGE, variables: { - first: 20, + first: 50, orgSlug: 'Wolf-Group', search: '', orderBy: { field: 'DOMAIN', direction: 'ASC' }, @@ -290,7 +288,13 @@ function mocks() { { request: { query: PAGINATED_ORG_AFFILIATIONS_ADMIN_PAGE, - variables: { orgSlug: 'Wolf-Group', first: 20, search: '', includePending: true }, + variables: { + orgSlug: 'Wolf-Group', + first: 50, + search: '', + includePending: true, + orderBy: { field: 'PERMISSION', direction: 'ASC' }, + }, }, result: { data: { diff --git a/frontend/src/admin/__tests__/AdminPanel.test.js b/frontend/src/admin/__tests__/AdminPanel.test.js index 10e4df227..c9ecfc7f0 100644 --- a/frontend/src/admin/__tests__/AdminPanel.test.js +++ b/frontend/src/admin/__tests__/AdminPanel.test.js @@ -2,32 +2,17 @@ import React from 'react' import { render, waitFor } from '@testing-library/react' import { theme, ChakraProvider } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { MockedProvider } from '@apollo/client/testing' -import { MemoryRouter, Route } from 'react-router-dom' +import { createMemoryRouter, RouterProvider } from 'react-router-dom' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' - import { AdminPanel } from '../AdminPanel' - import { createCache } from '../../client' import { UserVarProvider } from '../../utilities/userState' import { rawOrgDomainListData } from '../../fixtures/orgDomainListData' import { rawOrgUserListData } from '../../fixtures/orgUserListData' -import { - PAGINATED_ORG_AFFILIATIONS_ADMIN_PAGE, - PAGINATED_ORG_DOMAINS_ADMIN_PAGE, -} from '../../graphql/queries' - -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) +import { PAGINATED_ORG_AFFILIATIONS_ADMIN_PAGE, PAGINATED_ORG_DOMAINS_ADMIN_PAGE } from '../../graphql/queries' +import { TourProvider } from '../../userOnboarding/contexts/TourContext' const mocks = [ { @@ -46,24 +31,37 @@ const mocks = [ }, ] +const router = createMemoryRouter( + [ + { + path: '/reset-password/:resetToken', + element: ( + + ), + }, + ], + { + initialEntries: ['/reset-password/fwsdGDFSGSDVA.gedafbedafded.bgdbsedbeagbe'], + initialIndex: 0, + }, +) + describe('', () => { it('renders both a domain list and user list', async () => { const { getByText } = render( - + - - + + - - + + diff --git a/frontend/src/admin/__tests__/AuditLogTable.test.js b/frontend/src/admin/__tests__/AuditLogTable.test.js index 1317a30e2..d9860efe8 100644 --- a/frontend/src/admin/__tests__/AuditLogTable.test.js +++ b/frontend/src/admin/__tests__/AuditLogTable.test.js @@ -1,41 +1,24 @@ import React from 'react' import { theme, ChakraProvider } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { MockedProvider } from '@apollo/client/testing' import { AuditLogTable } from '../AuditLogTable' import { waitFor, render } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' import userEvent from '@testing-library/user-event' - import { UserVarProvider } from '../../utilities/userState' import { AUDIT_LOGS } from '../../graphql/queries' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) - describe('', () => { it('shows a table displaying activity logs from organizations', async () => { const { queryByText } = render( - + - + @@ -66,10 +49,7 @@ describe('', () => { > - + @@ -113,10 +93,7 @@ describe('', () => { > - + @@ -161,10 +138,7 @@ describe('', () => { > - + @@ -209,7 +183,7 @@ function mocks() { request: { query: AUDIT_LOGS, variables: { - first: 10, + first: 50, orderBy: { field: 'TIMESTAMP', direction: 'DESC', @@ -310,7 +284,7 @@ function mocks() { request: { query: AUDIT_LOGS, variables: { - first: 10, + first: 50, orderBy: { field: 'TIMESTAMP', direction: 'DESC', @@ -361,7 +335,7 @@ function mocks() { request: { query: AUDIT_LOGS, variables: { - first: 10, + first: 50, orderBy: { field: 'TIMESTAMP', direction: 'DESC', @@ -412,7 +386,7 @@ function mocks() { request: { query: AUDIT_LOGS, variables: { - first: 10, + first: 50, orderBy: { field: 'TIMESTAMP', direction: 'DESC', diff --git a/frontend/src/admin/__tests__/CvdEnrollmentForm.test.js b/frontend/src/admin/__tests__/CvdEnrollmentForm.test.js new file mode 100644 index 000000000..b5e64e55e --- /dev/null +++ b/frontend/src/admin/__tests__/CvdEnrollmentForm.test.js @@ -0,0 +1,138 @@ +import React from 'react' +import { render, screen, fireEvent } from '@testing-library/react' +import { ChakraProvider, theme } from '@chakra-ui/react' +import { I18nProvider } from '@lingui/react' +import { i18n } from '@lingui/core' +import { CvdEnrollmentForm } from '../CvdEnrollmentForm' +import '@testing-library/jest-dom' + +describe('', () => { + const baseValues = { + cvdEnrollment: { + status: 'NOT_ENROLLED', + description: '', + maxSeverity: '', + confidentialityRequirement: '', + integrityRequirement: '', + availabilityRequirement: '', + }, + } + const handleChange = jest.fn() + + function renderForm(props) { + return render( + + + + + , + ) + } + + afterEach(() => { + jest.clearAllMocks() + }) + + it('renders the enrollment status select and info popover', () => { + renderForm({ values: baseValues, handleChange, permission: 'USER' }) + expect(screen.getAllByText(/CVD Enrollment Status/i)[0]).toBeInTheDocument() + expect(screen.getByText(/More Info/i)).toBeInTheDocument() + expect(screen.getByText(/Not Enrolled/i)).toBeInTheDocument() + }) + + it('shows Pending option for ADMIN permission', () => { + renderForm({ + values: { cvdEnrollment: { ...baseValues.cvdEnrollment, status: 'PENDING' } }, + handleChange, + permission: 'ADMIN', + }) + // Use queryByText with fallback for option + const pendingOption = screen.queryByText((content, element) => { + return element.tagName && element.tagName.toLowerCase() === 'option' && /Pending/i.test(content) + }) + expect(pendingOption).not.toBeNull() + }) + + it('shows Enrolled option for OWNER permission', () => { + renderForm({ + values: { cvdEnrollment: { ...baseValues.cvdEnrollment, status: 'ENROLLED' } }, + handleChange, + permission: 'OWNER', + }) + expect(screen.getAllByText(/Enrolled/i)[0]).toBeInTheDocument() + }) + + it('renders additional fields when status is not NOT_ENROLLED', () => { + renderForm({ + values: { + cvdEnrollment: { + ...baseValues.cvdEnrollment, + status: 'ENROLLED', + description: 'desc', + maxSeverity: 'HIGH', + confidentialityRequirement: 'LOW', + integrityRequirement: 'HIGH', + availabilityRequirement: 'LOW', + }, + }, + handleChange, + permission: 'OWNER', + }) + expect(screen.getByLabelText(/Description/i)).toBeInTheDocument() + expect(screen.getByLabelText(/Max Severity/i)).toBeInTheDocument() + expect(screen.getByLabelText(/Confidentiality Requirement/i)).toBeInTheDocument() + expect(screen.getByLabelText(/Integrity Requirement/i)).toBeInTheDocument() + expect(screen.getByLabelText(/Availability Requirement/i)).toBeInTheDocument() + }) + + it('calls handleChange when status is changed', () => { + renderForm({ values: baseValues, handleChange, permission: 'ADMIN' }) + // Use getByRole to select by combobox and name + const select = screen.getByRole('combobox', { name: /CVD Enrollment Status/i }) + fireEvent.change(select, { + target: { value: 'PENDING' }, + }) + expect(handleChange).toHaveBeenCalled() + }) + + it('calls handleChange for description input', () => { + renderForm({ + values: { + cvdEnrollment: { ...baseValues.cvdEnrollment, status: 'ENROLLED' }, + }, + handleChange, + permission: 'OWNER', + }) + fireEvent.change(screen.getByLabelText(/Description/i), { + target: { value: 'Test description' }, + }) + expect(handleChange).toHaveBeenCalled() + }) + + it('handles empty values for select fields', () => { + renderForm({ + values: { + cvdEnrollment: { + status: 'ENROLLED', + description: '', + maxSeverity: '', + confidentialityRequirement: '', + integrityRequirement: '', + availabilityRequirement: '', + }, + }, + handleChange, + permission: 'OWNER', + }) + expect(screen.getByLabelText(/Max Severity/i).value).toBe('') + expect(screen.getByLabelText(/Confidentiality Requirement/i).value).toBe('') + expect(screen.getByLabelText(/Integrity Requirement/i).value).toBe('') + expect(screen.getByLabelText(/Availability Requirement/i).value).toBe('') + }) + + it('does not render additional fields when status is NOT_ENROLLED', () => { + renderForm({ values: baseValues, handleChange, permission: 'USER' }) + expect(screen.queryByLabelText(/Description/i)).not.toBeInTheDocument() + expect(screen.queryByLabelText(/Max Severity/i)).not.toBeInTheDocument() + }) +}) diff --git a/frontend/src/admin/__tests__/DomainTagsList.test.js b/frontend/src/admin/__tests__/DomainTagsList.test.js new file mode 100644 index 000000000..2b980e044 --- /dev/null +++ b/frontend/src/admin/__tests__/DomainTagsList.test.js @@ -0,0 +1,169 @@ +import React from 'react' +import { ChakraProvider, theme } from '@chakra-ui/react' +import { MemoryRouter } from 'react-router-dom' +import { I18nProvider } from '@lingui/react' +import { i18n } from '@lingui/core' +import { makeVar } from '@apollo/client' +import { render, screen, fireEvent, within } from '@testing-library/react' +import { MockedProvider } from '@apollo/client/testing' +import { DomainTagsList } from '../DomainTagsList' +import { DOMAIN_TAGS } from '../../graphql/queries' +import { CREATE_TAG, UPDATE_TAG } from '../../graphql/mutations' +import { UserVarProvider } from '../../utilities/userState' +import '@testing-library/jest-dom' + +jest.mock('@chakra-ui/react', () => { + const actual = jest.requireActual('@chakra-ui/react') + return { ...actual, useToast: () => jest.fn() } +}) + +jest.mock('@lingui/macro', () => ({ + t: (str) => str, + Trans: ({ children }) => children, +})) + +jest.mock('../../utilities/fieldRequirements', () => { + const Yup = require('yup') + return { + getRequirement: () => ({ required: true }), + schemaToValidation: () => + Yup.object().shape({ + labelEn: Yup.string().required(), + labelFr: Yup.string(), + isVisible: Yup.boolean(), + ownership: Yup.string(), + }), + } +}) + +jest.mock('../../components/LoadingMessage', () => ({ + LoadingMessage: () =>
Loading...
, +})) + +jest.mock('../../components/ErrorFallbackMessage', () => ({ + ErrorFallbackMessage: () =>
Error!
, +})) + +const mockTags = [ + { + tagId: '1', + label: 'Important', + description: 'Critical tag', + isVisible: true, + ownership: 'GLOBAL', + organizations: [], + }, + { + tagId: '2', + label: 'Archived', + description: 'No longer used', + isVisible: false, // so visibility icon appears + ownership: 'ORG', + organizations: [], + }, +] + +const mocks = [ + { + request: { query: DOMAIN_TAGS, variables: { orgId: undefined, isVisible: true } }, + result: { data: { findAllTags: mockTags } }, + }, + { + request: { + query: CREATE_TAG, + variables: { + labelEn: '', + labelFr: '', + descriptionEn: '', + descriptionFr: '', + isVisible: true, + ownership: '', + }, + }, + result: { + data: { createTag: { result: { __typename: 'Tag', tag: 'NewTag' } } }, + }, + }, + { + request: { + query: UPDATE_TAG, + variables: { tagId: '1', labelEn: 'Updated' }, + }, + result: { + data: { updateTag: { result: { __typename: 'Tag', tagId: '1' } } }, + }, + }, +] + +const noTagsMocks = [ + { + request: { query: DOMAIN_TAGS, variables: { orgId: undefined, isVisible: true } }, + result: { data: { findAllTags: [] } }, + }, +] + +function renderWithProviders(ui, { mocksOverride = [] } = {}) { + return render( + + + + + {ui} + + + + , + ) +} + +describe('DomainTagsList', () => { + it('renders loading state', () => { + renderWithProviders(, { mocksOverride: [] }) + expect(screen.getByTestId('loading')).toBeInTheDocument() + }) + + it('renders error state', async () => { + const errorMock = [ + { + request: { query: DOMAIN_TAGS, variables: { orgId: undefined, isVisible: true } }, + error: new Error('GraphQL error: Failed to fetch'), + }, + ] + renderWithProviders(, { mocksOverride: errorMock }) + expect(await screen.findByTestId('error')).toBeInTheDocument() + }) + + it('renders tags and allows toggling edit forms', async () => { + renderWithProviders() + expect(await screen.findByText(/IMPORTANT/i)).toBeInTheDocument() + expect(screen.getByText(/ARCHIVED/i)).toBeInTheDocument() + + const editButtons = screen.getAllByRole('button', { name: /edit tag/i }) + fireEvent.click(editButtons[0]) + expect(await screen.findAllByText(/visible/i)).not.toHaveLength(0) + }) + + it('opens and closes create form', async () => { + renderWithProviders() + const addTagBtn = await screen.findByRole('button', { name: /Add Tag/i }) + fireEvent.click(addTagBtn) + + const confirmBtn = await screen.findByRole('button', { name: /Confirm/i }) + const closeBtn = screen.getByRole('button', { name: /Close/i }) + fireEvent.click(closeBtn) + + expect(confirmBtn).not.toBeVisible() + }) + + it('shows "No Tags" when none returned', async () => { + renderWithProviders(, { mocksOverride: noTagsMocks }) + expect(await screen.findByText(/No Tags/i)).toBeInTheDocument() + }) + + it('toggles visibility icon for invisible tags', async () => { + renderWithProviders() + const archivedTag = await screen.findByText(/ARCHIVED/i) + const icon = within(archivedTag.closest('div')).getByLabelText('tag-invisible') + expect(icon).toBeInTheDocument() + }) +}) diff --git a/frontend/src/admin/__tests__/DomainUpdateList.test.js b/frontend/src/admin/__tests__/DomainUpdateList.test.js new file mode 100644 index 000000000..17f366512 --- /dev/null +++ b/frontend/src/admin/__tests__/DomainUpdateList.test.js @@ -0,0 +1,394 @@ +import React from 'react' +import { render, fireEvent, waitFor } from '@testing-library/react' +import { ChakraProvider, theme } from '@chakra-ui/react' +import { I18nProvider } from '@lingui/react' +import { i18n } from '@lingui/core' +import { MockedProvider } from '@apollo/client/testing' +import { DomainUpdateList } from '../DomainUpdateList' +import { UPDATE_DOMAINS_BY_DOMAIN_IDS, UPDATE_DOMAINS_BY_FILTERS } from '../../graphql/mutations' + +const domains = [ + { id: '1', domain: 'example.com', tags: ['tag1'] }, + { id: '2', domain: 'test.com', tags: ['tag2'] }, +] + +const availableTags = [ + { label: 'Tag 1', tagId: 'tag1' }, + { label: 'Tag 2', tagId: 'tag2' }, +] + +const filters = [] +const orgId = 'org-1' +const search = '' +const domainCount = 2 + +const mocks = [ + { + request: { + query: UPDATE_DOMAINS_BY_DOMAIN_IDS, + variables: { domainIds: ['1'], tags: ['tag1'], orgId }, + }, + result: { + data: { + updateDomainsByDomainIds: { + result: { + __typename: 'DomainBulkResult', + status: 'Success', + }, + }, + }, + }, + }, + // Added mock for select-all (multiple domainIds) + { + request: { + query: UPDATE_DOMAINS_BY_DOMAIN_IDS, + variables: { domainIds: ['1', '2'], tags: ['tag1'], orgId }, + }, + result: { + data: { + updateDomainsByDomainIds: { + result: { + __typename: 'DomainBulkResult', + status: 'Success', + }, + }, + }, + }, + }, + { + request: { + query: UPDATE_DOMAINS_BY_FILTERS, + variables: { filters, search, tags: ['tag1'], orgId }, + }, + result: { + data: { + updateDomainsByFilters: { + result: { + __typename: 'DomainBulkResult', + status: 'Success', + }, + }, + }, + }, + }, +] + +describe('', () => { + it('renders domain rows and select all', () => { + const { getByText, getByLabelText } = render( + + + + + + + , + ) + expect(getByText('example.com')).toBeInTheDocument() + expect(getByText('test.com')).toBeInTheDocument() + expect(getByLabelText(/Select All/i)).toBeInTheDocument() + }) + + it('selects a domain and opens tag drawer', async () => { + const { getAllByRole, getByText, getByRole } = render( + + + + + + + , + ) + const checkboxes = getAllByRole('checkbox') + fireEvent.click(checkboxes[1]) // select first domain + const tagBtn = getByRole('button', { name: /Tag Assets/i }) + fireEvent.click(tagBtn) + await waitFor(() => expect(getByText(/Apply Tags/i)).toBeInTheDocument()) + }) + + it('applies tags to selected domains', async () => { + const resetToFirstPage = jest.fn() + const { getAllByRole, getByRole, getByText, getByLabelText, _getByRole } = render( + + + + + + + , + ) + const checkboxes = getAllByRole('checkbox') + fireEvent.click(checkboxes[1]) // select first domain + fireEvent.click(getByRole('button', { name: /Tag Assets/i })) + await waitFor(() => getByText(/Apply Tags/i)) + fireEvent.click(getByLabelText('TAG 1')) + fireEvent.click(getByRole('button', { name: /Apply/i })) + await waitFor(() => getByText(/Are you sure\?/i)) + fireEvent.click(getByRole('button', { name: /Yes, Apply/i })) + await waitFor(() => getByText(/Domains updated/i)) + }) + + it('applies tags to all filtered domains when select all is checked', async () => { + const resetToFirstPage = jest.fn() + const { getAllByRole, getByRole, getByText, getByLabelText } = render( + + + + + + + , + ) + const selectAll = getAllByRole('checkbox')[0] + fireEvent.click(selectAll) + fireEvent.click(getByRole('button', { name: /Tag Assets/i })) + await waitFor(() => getByText(/Apply Tags/i)) + fireEvent.click(getByLabelText('TAG 1')) + fireEvent.click(getByRole('button', { name: /Apply/i })) + await waitFor(() => getByText(/Are you sure\?/i)) + fireEvent.click(getByRole('button', { name: /Yes, Apply/i })) + await waitFor(() => { + expect(document.body.textContent).toMatch(/Domains updated/i) + }) + }) + + it('shows error toast on mutation error', async () => { + const errorMocks = [ + { + request: { + query: UPDATE_DOMAINS_BY_DOMAIN_IDS, + variables: { domainIds: ['1'], tags: ['tag1'], orgId }, + }, + error: new Error('Test error'), + }, + ] + const { getAllByRole, getByText, getByRole, getByLabelText } = render( + + + + + + + , + ) + const checkboxes = getAllByRole('checkbox') + fireEvent.click(checkboxes[1]) + fireEvent.click(getByRole('button', { name: 'Tag Assets' })) + await waitFor(() => getByText(/Apply Tags/i)) + fireEvent.click(getByLabelText('TAG 1')) + fireEvent.click(getByRole('button', { name: 'Apply' })) + await waitFor(() => getByText(/Are you sure\?/i)) + fireEvent.click(getByRole('button', { name: 'Yes, Apply' })) + await waitFor(() => getByText(/An error occurred/i)) + }) + + it('selects multiple domains across pages and preserves selection', () => { + // Simulate two pages of domains + const page1 = [ + { id: '1', domain: 'example.com', tags: ['tag1'] }, + { id: '2', domain: 'test.com', tags: ['tag2'] }, + ] + const page2 = [ + { id: '3', domain: 'foo.com', tags: ['tag1'] }, + { id: '4', domain: 'bar.com', tags: ['tag2'] }, + ] + // Render page 1 + const { getAllByRole, rerender, getByText, getAllByText } = render( + + + + + + + , + ) + // Select first domain on page 1 + const checkboxes1 = getAllByRole('checkbox') + fireEvent.click(checkboxes1[1]) + expect(getByText(/1 selected on this page/i)).toBeInTheDocument() + // Go to page 2 + rerender( + + + + + + + , + ) + // Select all on page 2 + const checkboxes2 = getAllByRole('checkbox') + fireEvent.click(checkboxes2[0]) // select all on page 2 + // Only match the visible banner, not the aria-live region + const visibleBanner = getAllByText(/All 2 domains on this page are selected/i).find( + (el) => el.tagName.toLowerCase() === 'p', + ) + expect(visibleBanner).toBeInTheDocument() + // Go back to page 1, both selections should persist + rerender( + + + + + + + , + ) + // The first domain should still be selected + expect(getByText(/1 selected on this page/i)).toBeInTheDocument() + }) + + it('shows indeterminate state when some but not all on page are selected', () => { + const { getAllByRole, getByText } = render( + + + + + + + , + ) + // Select only one domain + const checkboxes = getAllByRole('checkbox') + fireEvent.click(checkboxes[1]) + // The select all checkbox should be indeterminate + expect(checkboxes[0].indeterminate).toBe(true) + expect(getByText(/1 selected on this page/i)).toBeInTheDocument() + }) + + it('select all on page only adds those domains to selection, not clearing others', () => { + // page 1 + const page1 = [ + { id: '1', domain: 'example.com', tags: ['tag1'] }, + { id: '2', domain: 'test.com', tags: ['tag2'] }, + ] + // page 2 + const page2 = [ + { id: '3', domain: 'foo.com', tags: ['tag1'] }, + { id: '4', domain: 'bar.com', tags: ['tag2'] }, + ] + const { getAllByRole, rerender, getByText } = render( + + + + + + + , + ) + // Select first domain on page 1 + const checkboxes1 = getAllByRole('checkbox') + fireEvent.click(checkboxes1[1]) + // Go to page 2 and select all + rerender( + + + + + + + , + ) + const checkboxes2 = getAllByRole('checkbox') + fireEvent.click(checkboxes2[0]) // select all on page 2 + // Go back to page 1, both selections should persist + rerender( + + + + + + + , + ) + // Both domains on page 1 should be selectable, and the first should still be selected + expect(getByText(/1 selected on this page/i)).toBeInTheDocument() + }) +}) diff --git a/frontend/src/admin/__tests__/OrganizationInformation.test.js b/frontend/src/admin/__tests__/OrganizationInformation.test.js index 36fa3ed44..279bdf120 100644 --- a/frontend/src/admin/__tests__/OrganizationInformation.test.js +++ b/frontend/src/admin/__tests__/OrganizationInformation.test.js @@ -1,7 +1,7 @@ import React from 'react' import { ChakraProvider, theme } from '@chakra-ui/react' import { MemoryRouter } from 'react-router-dom' -import { render, waitFor } from '@testing-library/react' +import { act, render, waitFor } from '@testing-library/react' import { MockedProvider } from '@apollo/client/testing' import { I18nProvider } from '@lingui/react' import { i18n } from '@lingui/core' @@ -39,6 +39,11 @@ const mocks = [ province: 'org province', city: 'org city', verified: true, + externalId: 'ORG123', + policies: { + psd: true, + pgs: true, + }, __typename: 'Organization', }, }, @@ -70,8 +75,8 @@ const mocks = [ query: UPDATE_ORGANIZATION, variables: { id: 'org-id', - acronymEN: 'NEWACREN', - acronymFR: 'NEWACRFR', + nameEN: 'NEW ACREN', + nameFR: 'NEW ACRFR', countryEN: 'Canada', }, }, @@ -80,7 +85,7 @@ const mocks = [ updateOrganization: { result: { id: 'org-id', - acronym: 'NEWACREN', + acronym: 'NEW ACREN', name: 'Org Name', slug: 'org-name', zone: 'org zone', @@ -89,6 +94,11 @@ const mocks = [ province: 'org province', city: 'org city', verified: true, + externalId: 'ORG123', + policies: { + psd: true, + pgs: true, + }, __typename: 'Organization', }, }, @@ -172,6 +182,11 @@ describe('', () => { province: 'org province', city: 'org city', verified: true, + externalId: 'ORG123', + policies: { + psd: true, + pgs: true, + }, __typename: 'Organization', }, }, @@ -256,6 +271,11 @@ describe('', () => { province: 'org province', city: 'org city', verified: true, + externalId: 'ORG123', + policies: { + psd: true, + pgs: true, + }, __typename: 'Organization', }, }, @@ -353,6 +373,11 @@ describe('', () => { province: 'org province', city: 'org city', verified: true, + externalId: 'ORG123', + policies: { + psd: true, + pgs: true, + }, __typename: 'Organization', }, }, @@ -381,8 +406,8 @@ describe('', () => { query: UPDATE_ORGANIZATION, variables: { id: 'org-id', - acronymEN: 'NEWACREN', - acronymFR: 'NEWACRFR', + nameEN: 'NEW ACREN', + nameFR: 'NEWA CRFR', countryEN: 'Canada', }, }, @@ -391,7 +416,7 @@ describe('', () => { updateOrganization: { result: { id: 'org-id', - acronym: 'NEWACREN', + acronym: 'NEW ACREN', name: 'Org Name', slug: 'org-name', zone: 'org zone', @@ -400,6 +425,11 @@ describe('', () => { province: 'org province', city: 'org city', verified: true, + externalId: 'ORG123', + policies: { + psd: true, + pgs: true, + }, __typename: 'Organization', }, }, @@ -517,11 +547,7 @@ describe('', () => { - {}} - /> + {}} /> @@ -539,11 +565,11 @@ describe('', () => { await waitFor(() => expect(getByText(/Blank fields will not be included/)).toBeVisible()) - const acronymENInput = await findByRole('textbox', { - name: 'Acronym (EN)', + const nameENInput = await findByRole('textbox', { + name: 'Name (EN)', }) - const acronymFRInput = getByRole('textbox', { - name: 'Acronym (FR)', + const nameFRInput = getByRole('textbox', { + name: 'Name (FR)', }) const countryENInput = getByRole('textbox', { name: 'Country (EN)', @@ -552,11 +578,13 @@ describe('', () => { name: 'Confirm', }) - userEvent.type(acronymENInput, 'NEWACREN') - userEvent.type(acronymFRInput, 'NEWACRFR') + userEvent.type(nameENInput, 'NEW ACREN') + userEvent.type(nameFRInput, 'NEW ACRFR') userEvent.type(countryENInput, 'Canada') - userEvent.click(confrimOrganizationUpdateButton) + await act(() => { + userEvent.click(confrimOrganizationUpdateButton) + }) const successfulUpdateToastText = await findByText(/You have successfully updated Org Name/) @@ -587,6 +615,11 @@ describe('', () => { province: 'org province', city: 'org city', verified: true, + externalId: 'ORG123', + policies: { + psd: true, + pgs: true, + }, __typename: 'Organization', }, }, @@ -618,8 +651,8 @@ describe('', () => { query: UPDATE_ORGANIZATION, variables: { id: 'org-id', - acronymEN: 'NEWACREN', - acronymFR: 'NEWACRFR', + nameEN: 'NEW ACREN', + nameFR: 'NEW ACRFR', countryEN: 'Canada', }, }, @@ -649,11 +682,7 @@ describe('', () => { - {}} - /> + {}} /> @@ -671,11 +700,11 @@ describe('', () => { await waitFor(() => expect(getByText(/Blank fields will not be included/)).toBeVisible()) - const acronymENInput = await findByRole('textbox', { - name: 'Acronym (EN)', + const nameENInput = await findByRole('textbox', { + name: 'Name (EN)', }) - const acronymFRInput = getByRole('textbox', { - name: 'Acronym (FR)', + const nameFRInput = getByRole('textbox', { + name: 'Name (FR)', }) const countryENInput = getByRole('textbox', { name: 'Country (EN)', @@ -684,8 +713,8 @@ describe('', () => { name: 'Confirm', }) - userEvent.type(acronymENInput, 'NEWACREN') - userEvent.type(acronymFRInput, 'NEWACRFR') + userEvent.type(nameENInput, 'NEW ACREN') + userEvent.type(nameFRInput, 'NEW ACRFR') userEvent.type(countryENInput, 'Canada') userEvent.click(confrimOrganizationUpdateButton) diff --git a/frontend/src/admin/__tests__/SuperAdminUserList.test.js b/frontend/src/admin/__tests__/SuperAdminUserList.test.js index 16bbb46b3..75a2dccb4 100644 --- a/frontend/src/admin/__tests__/SuperAdminUserList.test.js +++ b/frontend/src/admin/__tests__/SuperAdminUserList.test.js @@ -4,27 +4,14 @@ import { MemoryRouter } from 'react-router-dom' import { theme, ChakraProvider } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' import { MockedProvider } from '@apollo/client/testing' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' - import { SuperAdminUserList } from '../SuperAdminUserList' - import { UserVarProvider } from '../../utilities/userState' import { createCache } from '../../client' import { FIND_MY_USERS } from '../../graphql/queries' import { UPDATE_USER_ROLE, REMOVE_USER_FROM_ORG } from '../../graphql/mutations' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) - const saUserListMockData = { data: { findMyUsers: { @@ -135,7 +122,7 @@ const successMocks = [ request: { query: FIND_MY_USERS, variables: { - first: 10, + first: 50, orderBy: { field: 'USER_USERNAME', direction: 'ASC' }, search: '', }, @@ -147,9 +134,7 @@ const successMocks = [ query: UPDATE_USER_ROLE, variables: { userName: saUserListMockData.data.findMyUsers.edges[0].node.userName, - orgId: - saUserListMockData.data.findMyUsers.edges[0].node.affiliations - .edges[0].node.organization.id, + orgId: saUserListMockData.data.findMyUsers.edges[0].node.affiliations.edges[0].node.organization.id, role: 'ADMIN', }, }, @@ -170,9 +155,7 @@ const successMocks = [ query: REMOVE_USER_FROM_ORG, variables: { userId: saUserListMockData.data.findMyUsers.edges[0].node.id, - orgId: - saUserListMockData.data.findMyUsers.edges[0].node.affiliations - .edges[0].node.organization.id, + orgId: saUserListMockData.data.findMyUsers.edges[0].node.affiliations.edges[0].node.organization.id, }, }, result: { @@ -197,9 +180,7 @@ describe('', () => { it('successfully renders with mocked data', async () => { const { queryByText } = render( - + @@ -213,9 +194,7 @@ describe('', () => { // wait for query to load await waitFor(() => expect(queryByText(/search/i)).toBeInTheDocument()) // get user data - await waitFor(() => - expect(queryByText(/Jonathan Kassulke/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Jonathan Kassulke/i)).toBeInTheDocument()) }) describe('individual user cards', () => { it('are clickable', async () => { @@ -241,9 +220,7 @@ describe('', () => { // wait for query to load await waitFor(() => expect(queryByText(/search/i)).toBeInTheDocument()) // get user data - await waitFor(() => - expect(queryByText(/Raegan.Ritchie50@yahoo.com/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Raegan.Ritchie50@yahoo.com/i)).toBeInTheDocument()) const userCard1 = getByRole('button', { name: /Raegan.Ritchie50@yahoo.com/i, }) @@ -272,23 +249,15 @@ describe('', () => { , ) // wait for query to load - await waitFor(() => - expect(queryByText(/search/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/search/i)).toBeInTheDocument()) // get user data - await waitFor(() => - expect( - queryByText(/Raegan.Ritchie50@yahoo.com/i), - ).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Raegan.Ritchie50@yahoo.com/i)).toBeInTheDocument()) const userCard1 = getByRole('button', { name: /Raegan.Ritchie50@yahoo.com/i, }) fireEvent.click(userCard1) - await waitFor(() => - expect(queryByText(/Kreiger - Schamberger/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Kreiger - Schamberger/i)).toBeInTheDocument()) }) describe('admin abilities', () => { @@ -313,29 +282,19 @@ describe('', () => {
, ) // wait for query to load - await waitFor(() => - expect(queryByText(/search/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/search/i)).toBeInTheDocument()) // get user data - await waitFor(() => - expect( - queryByText(/Raegan.Ritchie50@yahoo.com/i), - ).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Raegan.Ritchie50@yahoo.com/i)).toBeInTheDocument()) const userCard1 = getByRole('button', { name: /Raegan.Ritchie50@yahoo.com/i, }) fireEvent.click(userCard1) - await waitFor(() => - expect(queryByText(/Kreiger - Schamberger/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Kreiger - Schamberger/i)).toBeInTheDocument()) const editBtn = getByRole('button', { name: 'Edit Raegan.Ritchie50@yahoo.com in Kreiger - Schamberger', }) fireEvent.click(editBtn) - await waitFor(() => - expect(queryAllByText(/Edit User/i)[0]).toBeInTheDocument(), - ) + await waitFor(() => expect(queryAllByText(/Edit User/i)[0]).toBeInTheDocument()) }) it('remove user from org', async () => { const { queryByText, getByRole, queryAllByText } = render( @@ -358,22 +317,14 @@ describe('', () => {
, ) // wait for query to load - await waitFor(() => - expect(queryByText(/search/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/search/i)).toBeInTheDocument()) // get user data - await waitFor(() => - expect( - queryByText(/Raegan.Ritchie50@yahoo.com/i), - ).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Raegan.Ritchie50@yahoo.com/i)).toBeInTheDocument()) const userCard1 = getByRole('button', { name: /Raegan.Ritchie50@yahoo.com/i, }) fireEvent.click(userCard1) - await waitFor(() => - expect(queryByText(/Kreiger - Schamberger/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Kreiger - Schamberger/i)).toBeInTheDocument()) const removeBtn = getByRole('button', { name: 'Remove Raegan.Ritchie50@yahoo.com from Kreiger - Schamberger', }) @@ -404,26 +355,16 @@ describe('', () => {
, ) // wait for query to load - await waitFor(() => - expect(queryByText(/search/i)).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/search/i)).toBeInTheDocument()) // get user data - await waitFor(() => - expect( - queryByText(/Raegan.Ritchie50@yahoo.com/i), - ).toBeInTheDocument(), - ) + await waitFor(() => expect(queryByText(/Raegan.Ritchie50@yahoo.com/i)).toBeInTheDocument()) const userCard1 = getByRole('button', { name: /Raegan.Ritchie50@yahoo.com/i, }) fireEvent.click(userCard1) await waitFor(() => - expect( - queryByText( - /This user is not affiliated with any organizations/i, - ), - ).toBeInTheDocument(), + expect(queryByText(/This user is not affiliated with any organizations/i)).toBeInTheDocument(), ) }) }) diff --git a/frontend/src/admin/__tests__/TagForm.test.js b/frontend/src/admin/__tests__/TagForm.test.js new file mode 100644 index 000000000..cc907b7c5 --- /dev/null +++ b/frontend/src/admin/__tests__/TagForm.test.js @@ -0,0 +1,175 @@ +import React from 'react' +import { render, screen, fireEvent, waitFor } from '@testing-library/react' +import { MockedProvider } from '@apollo/client/testing' +import { ChakraProvider, theme } from '@chakra-ui/react' +import { I18nProvider } from '@lingui/react' +import { i18n } from '@lingui/core' +import { TagForm } from '../TagForm' +import { CREATE_TAG, UPDATE_TAG } from '../../graphql/mutations' +import '@testing-library/jest-dom' + +jest.mock('@chakra-ui/react', () => { + const actual = jest.requireActual('@chakra-ui/react') + return { + ...actual, + useToast: () => jest.fn(), + } +}) + +jest.mock('@lingui/macro', () => ({ + t: (str) => str, + Trans: ({ children }) => children, +})) + +jest.mock('../../utilities/fieldRequirements', () => { + const Yup = require('yup') + return { + getRequirement: () => ({ required: true }), + schemaToValidation: () => + Yup.object().shape({ + labelEn: Yup.string().required(), + labelFr: Yup.string(), + isVisible: Yup.boolean(), + ownership: Yup.string(), + }), + } +}) + +describe('', () => { + const setTagFormState = jest.fn() + const defaultProps = { + visible: true, + ownership: '', + setTagFormState, + } + + afterEach(() => { + jest.clearAllMocks() + }) + + it('renders create form and allows input', async () => { + render( + + + + + + + , + ) + expect(screen.getByLabelText(/Label \(EN\)/i)).toBeInTheDocument() + fireEvent.change(screen.getByLabelText(/Label \(EN\)/i), { target: { value: 'TestTag' } }) + expect(screen.getByLabelText(/Label \(EN\)/i)).toHaveValue('TestTag') + }) + + it('renders update form and allows input', async () => { + render( + + + + + + + , + ) + expect(screen.getByLabelText(/Label \(EN\)/i)).toBeInTheDocument() + fireEvent.change(screen.getByLabelText(/Label \(EN\)/i), { target: { value: 'UpdatedTag' } }) + expect(screen.getByLabelText(/Label \(EN\)/i)).toHaveValue('UpdatedTag') + }) + + it('calls setTagFormState on Close (create)', async () => { + render( + + + + + + + , + ) + fireEvent.click(screen.getByText(/Close/i)) + expect(setTagFormState).toHaveBeenCalledWith(expect.any(Function)) + }) + + it('calls setTagFormState on Close (update)', async () => { + render( + + + + + + + , + ) + fireEvent.click(screen.getByText(/Close/i)) + expect(setTagFormState).toHaveBeenCalledWith(expect.any(Function)) + }) + + it('submits create form and disables button while loading', async () => { + const mocks = [ + { + request: { + query: CREATE_TAG, + variables: { + labelEn: 'TestTag', + labelFr: '', + descriptionEn: '', + descriptionFr: '', + isVisible: true, + ownership: '', + }, + }, + result: { + data: { + createTag: { + result: { __typename: 'Tag', tag: 'TestTag' }, + }, + }, + }, + }, + ] + render( + + + + + + + , + ) + fireEvent.change(screen.getByLabelText(/Label \(EN\)/i), { target: { value: 'TestTag' } }) + fireEvent.click(screen.getByText(/Confirm/i)) + await waitFor(() => expect(setTagFormState).toHaveBeenCalled()) + }) + + it('calls handleReset on Clear', async () => { + render( + + + + + + + , + ) + const clearBtn = screen.getByText(/Clear/i) + fireEvent.click(clearBtn) + // No error = pass, Formik handles reset + }) + + it('shows validation error if required field is empty', async () => { + render( + + + + + + + , + ) + fireEvent.click(screen.getByText(/Confirm/i)) + await waitFor(() => { + expect(screen.getByLabelText(/Label \(EN\)/i)).toBeInvalid() + }) + }) +}) diff --git a/frontend/src/admin/__tests__/UserList.test.js b/frontend/src/admin/__tests__/UserList.test.js index 8cf2f8339..418457bc2 100644 --- a/frontend/src/admin/__tests__/UserList.test.js +++ b/frontend/src/admin/__tests__/UserList.test.js @@ -4,40 +4,39 @@ import { MemoryRouter } from 'react-router-dom' import { theme, ChakraProvider } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' import { MockedProvider } from '@apollo/client/testing' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' - import { UserList } from '../UserList' - import { UserVarProvider } from '../../utilities/userState' import { createCache } from '../../client' import { PAGINATED_ORG_AFFILIATIONS_ADMIN_PAGE as FORWARD } from '../../graphql/queries' import { UPDATE_USER_ROLE, INVITE_USER_TO_ORG, REMOVE_USER_FROM_ORG } from '../../graphql/mutations' import { rawOrgUserListData } from '../../fixtures/orgUserListData' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) - const successMocks = [ { request: { query: FORWARD, - variables: { first: 20, orgSlug: 'test-org.slug', search: '', includePending: true }, + variables: { + first: 50, + orgSlug: 'test-org.slug', + search: '', + includePending: true, + orderBy: { direction: 'ASC', field: 'PERMISSION' }, + }, }, result: { data: rawOrgUserListData }, }, { request: { query: FORWARD, - variables: { first: 20, orgSlug: 'test-org.slug', search: '', includePending: true }, + variables: { + first: 50, + orgSlug: 'test-org.slug', + search: '', + includePending: true, + orderBy: { direction: 'ASC', field: 'PERMISSION' }, + }, }, result: { data: rawOrgUserListData }, }, @@ -69,7 +68,6 @@ const successMocks = [ userName: 'newUser@test.ca', requestedRole: 'USER', orgId: rawOrgUserListData.findOrganizationBySlug.id, - preferredLang: 'ENGLISH', }, }, result: { @@ -131,11 +129,7 @@ describe('', () => {
, ) - await waitFor(() => - expect( - getByText(rawOrgUserListData.findOrganizationBySlug.affiliations.edges[0].node.user.userName), - ).toBeInTheDocument(), - ) + await waitFor(() => expect(getByText(/test.user@email.com/i)).toBeInTheDocument()) }) describe('Admin profile userlist', () => { @@ -216,7 +210,7 @@ describe('', () => { diff --git a/frontend/src/admin/__tests__/UserListModal.test.js b/frontend/src/admin/__tests__/UserListModal.test.js index 903f9ade0..d21c83084 100644 --- a/frontend/src/admin/__tests__/UserListModal.test.js +++ b/frontend/src/admin/__tests__/UserListModal.test.js @@ -4,27 +4,14 @@ import { MemoryRouter } from 'react-router-dom' import { theme, ChakraProvider, useDisclosure } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' import { MockedProvider } from '@apollo/client/testing' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' - import { UserVarProvider } from '../../utilities/userState' import { createCache } from '../../client' import { UPDATE_USER_ROLE, INVITE_USER_TO_ORG, REMOVE_USER_FROM_ORG } from '../../graphql/mutations' import { UserListModal } from '../UserListModal' import userEvent from '@testing-library/user-event' import canada from '../../theme/canada' - -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) - const orgId = 'test-id' const editingUserId = 'test-id' const editingUserName = 'test-username@email.com' diff --git a/frontend/src/app/ABTestWrapper.js b/frontend/src/app/ABTestWrapper.js index 9c7d45ab6..6d1177b92 100644 --- a/frontend/src/app/ABTestWrapper.js +++ b/frontend/src/app/ABTestWrapper.js @@ -1,30 +1,20 @@ import React from 'react' import { any, string } from 'prop-types' import { useUserVar } from '../utilities/userState' -import { IS_USER_SUPER_ADMIN } from '../graphql/queries' -import { useQuery } from '@apollo/client' - -const isInsiderUser = ({ isUserSuperAdmin, insideUser }) => { - return insideUser || isUserSuperAdmin -} export function ABTestVariant({ children }) { return <>{children} } export function ABTestWrapper({ children, insiderVariantName = 'B' }) { - const { data } = useQuery(IS_USER_SUPER_ADMIN) - const { currentUser } = useUserVar() + const { + currentUser: { insideUser }, + } = useUserVar() let childIndex = 0 // only one variant if (!children.length) { - if ( - isInsiderUser({ - isUserSuperAdmin: data?.isUserSuperAdmin || false, - insideUser: currentUser?.insideUser || false, - }) - ) { + if (insideUser) { if (children.props.name === insiderVariantName) return <>{children} else return <> } else { @@ -33,12 +23,7 @@ export function ABTestWrapper({ children, insiderVariantName = 'B' }) { } } // A + B variants - if ( - isInsiderUser({ - isUserSuperAdmin: data?.isUserSuperAdmin || false, - insideUser: currentUser?.insideUser || false, - }) - ) { + if (insideUser) { childIndex = children.findIndex((variant) => variant.props.name === insiderVariantName) } else { childIndex = children.findIndex((variant) => variant.props.name !== insiderVariantName) diff --git a/frontend/src/app/App.js b/frontend/src/app/App.js index cf92f405a..b0cf9ebc1 100644 --- a/frontend/src/app/App.js +++ b/frontend/src/app/App.js @@ -1,9 +1,8 @@ -import React, { Suspense, useEffect } from 'react' -import { Switch, Link as RouteLink, Redirect, useLocation } from 'react-router-dom' -import { CSSReset, Flex, Link, Text } from '@chakra-ui/react' -import { t, Trans } from '@lingui/macro' -import { ErrorBoundary } from 'react-error-boundary' -import { useQuery } from '@apollo/client' +import React, { Suspense } from 'react' +import { Link as RouteLink, Routes, Route, Navigate } from 'react-router-dom' +import { AlertDescription, AlertTitle, Box, Code, CSSReset, Flex, Link, Skeleton, Text } from '@chakra-ui/react' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" import { Main } from './Main' import { TopBanner } from './TopBanner' @@ -11,19 +10,18 @@ import { Footer } from './Footer' import { Navigation } from './Navigation' import { SkipLink } from './SkipLink' import { FloatingMenu } from './FloatingMenu' -import { PrivatePage } from './PrivatePage' -import { Page } from './Page' -import { wsClient } from '../client' import { LoadingMessage } from '../components/LoadingMessage' -import { ErrorFallbackMessage } from '../components/ErrorFallbackMessage' import { useUserVar } from '../utilities/userState' import { lazyWithRetry } from '../utilities/lazyWithRetry' import { LandingPage } from '../landing/LandingPage' import { NotificationBanner } from './NotificationBanner' -import { IS_LOGIN_REQUIRED } from '../graphql/queries' import { useLingui } from '@lingui/react' +import { ScrollToAnchor } from './ScrollToAnchor' +import { bool } from 'prop-types' +import { Page } from './Page' +import { PrivatePage } from './PrivatePage' const GuidancePage = lazyWithRetry(() => import('../guidance/GuidancePage')) const PageNotFound = lazyWithRetry(() => import('./PageNotFound')) @@ -46,28 +44,17 @@ const ContactUsPage = lazyWithRetry(() => import('./ContactUsPage')) const ReadGuidancePage = lazyWithRetry(() => import('./ReadGuidancePage')) const MyTrackerPage = lazyWithRetry(() => import('../user/MyTrackerPage')) -export function App() { +export function App({ initialLoading, isLoginRequired }) { // Hooks to be used with this functional component - const { currentUser, isLoggedIn, isEmailValidated, currentTFAMethod, isAffiliated } = useUserVar() + const { currentUser, isLoggedIn, isEmailValidated, currentTFAMethod, hasAffiliation } = useUserVar() const { i18n } = useLingui() - const { data } = useQuery(IS_LOGIN_REQUIRED, {}) - const location = useLocation() - - // Close websocket on user jwt change (refresh/logout) - // Ready state documented at: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState - useEffect(() => { - // User is logged out and websocket connection is active - if (currentUser?.jwt === '' && [0, 1].includes(wsClient.status)) { - wsClient.close() - } - }, [currentUser.jwt]) const notificationBanner = () => { if (isLoggedIn()) { if (isEmailValidated()) { if (currentTFAMethod() === 'NONE') { return ( - + To maximize your account's security,{' '} @@ -80,9 +67,9 @@ export function App() { ) } - if (!isAffiliated()) { + if (!hasAffiliation()) { return ( - + To view detailed scan results and other functionality,{' '} @@ -97,7 +84,7 @@ export function App() { } } else { return ( - + To enable full app functionality and maximize your account's security,{' '} @@ -115,218 +102,312 @@ export function App() { return ( +
Skip to main content - +
Home - {((isLoggedIn() && isEmailValidated()) || !data?.loginRequired) && ( + {initialLoading ? ( <> - - Organizations - - - Domains - + + LoadingState + + + LoadingState + - )} - - {isLoggedIn() && isEmailValidated() && currentTFAMethod() !== 'NONE' && ( - - DMARC Summaries - - )} - - {isLoggedIn() && ( + ) : ( <> - - myTracker - - - - Account Settings - - - )} - - {isLoggedIn() && isEmailValidated() && currentTFAMethod() !== 'NONE' && ( - <> - - Admin Profile - + {((isLoggedIn() && isEmailValidated()) || !isLoginRequired) && ( + <> + + Organizations + + + Domains + + + )} + + {isLoggedIn() && isEmailValidated() && currentTFAMethod() !== 'NONE' && ( + + DMARC Summaries + + )} + + {isLoggedIn() && ( + <> + + myTracker + + + + Account Settings + + + )} + + {isLoggedIn() && isEmailValidated() && currentTFAMethod() !== 'NONE' && ( + <> + + Admin Profile + + + )} )} + {window.env?.APP_IS_PRODUCTION !== true ? ( + + + + You are currently visiting a staging environment, used for testing purposes only. This is{' '} + not the live production site. +
+ Visit the production site at{' '} + + https://tracker.canada.ca + {' '} + (GC network only). +
+
+
+ ) : null} + {notificationBanner()} + + + + Tracker now automatically manages your DKIM selectors. + + + + Manual management of DKIM selectors is discontinued. DKIM selectors will automatically be added when + setting rua=mailto:dmarc@cyber.gc.ca in your DMARC record.{' '} + + Learn more + + . + + + + +
- }> - - - - - - - - - - { - return isLoggedIn() ? ( - - ) : ( - - ) - }} - /> - - - - - - - - - - - - - - - {() => ( - - - - )} - - - - {() => ( - - - - )} - - - - {isLoggedIn() && isEmailValidated() && currentTFAMethod() !== 'NONE' ? ( - - ) : ( - - )} - - - - {() => ( - - - - )} - - - - {() => ( - - - - )} - - - - {() => ( - - - - )} - - - - {() => ( - - - - )} - - - - {isLoggedIn() ? ( - - ) : ( - - )} - - - - {isLoggedIn() ? ( - - ) : ( - - )} - - - - {() => } - - - - {isLoggedIn() && isEmailValidated() && currentTFAMethod() !== 'NONE' ? ( - - ) : ( - - )} - - - - - + {initialLoading ? ( + + ) : ( + }> + + + + + } + exact + path="/" + /> + + + + + } + path="/create-user/:userOrgToken?" + /> + + : } /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + )}
@@ -347,18 +428,19 @@ export function App() { Terms & conditions - - Report an Issue - - - + Contact Us - + Guidance
) } + +App.propTypes = { + initialLoading: bool, + isLoginRequired: bool, +} diff --git a/frontend/src/app/ContactUsPage.js b/frontend/src/app/ContactUsPage.js index da19609f9..7ac31800b 100644 --- a/frontend/src/app/ContactUsPage.js +++ b/frontend/src/app/ContactUsPage.js @@ -1,6 +1,7 @@ import React from 'react' -import { Trans } from '@lingui/macro' +import { Trans } from "@lingui/react/macro" import { Box, Divider, Heading, Button, Text, Link } from '@chakra-ui/react' +import { Link as RouteLink } from 'react-router-dom' export default function ContactUsPage() { return ( @@ -9,35 +10,63 @@ export default function ContactUsPage() { Contact the Tracker Team - - - Government of Canada Employees - - - - Individuals from a departmental information technology group may - contact the TBS Cyber Security mailbox for results interpretation - and domain management. - - - - - Note that compliance data does not automatically refresh. - Modifications to domains could take 24 hours to update. - - - + + + + Read Guidance + + + + The{' '} + + "Getting Started" guide and FAQ section + {' '} + will help you to understand Tracker's capabilities and limitations. + + + + + + + Government of Canada Employees + + + + Individuals from a departmental information technology group may contact the TBS Cyber Security mailbox + for results interpretation and domain management. + + + + + For questions and issues related to scan data, your organization's domain list, or getting help onboarding + users, please contact TBS Cyber Security. + + + + + + + + Tracker GitHub + + + + Submit bug reports and feature requests through our{' '} + + GitHub page + + . + + + ) diff --git a/frontend/src/app/FloatingMenu.js b/frontend/src/app/FloatingMenu.js index 24dbeecfc..e7efbce81 100644 --- a/frontend/src/app/FloatingMenu.js +++ b/frontend/src/app/FloatingMenu.js @@ -18,7 +18,8 @@ import { useDisclosure, useToast, } from '@chakra-ui/react' -import { t, Trans } from '@lingui/macro' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" import { useLingui } from '@lingui/react' import { useMutation } from '@apollo/client' diff --git a/frontend/src/app/LocaleSwitcher.js b/frontend/src/app/LocaleSwitcher.js index d8aec5142..b0156102b 100644 --- a/frontend/src/app/LocaleSwitcher.js +++ b/frontend/src/app/LocaleSwitcher.js @@ -30,7 +30,7 @@ const Toggler = (props) => { borderWidth="1px" borderColor="gray.300" aria-hidden - d="flex" + display="flex" color="primary" textTransform="uppercase" fontWeight="bold" @@ -47,10 +47,10 @@ const Toggler = (props) => { ) } -export function LocaleSwitcher() { +export function LocaleSwitcher({ ...props }) { const { i18n } = useLingui() return ( - + {i18n.locale === 'en' && } {i18n.locale === 'fr' && } diff --git a/frontend/src/app/NotificationBanner.js b/frontend/src/app/NotificationBanner.js index 66ed65e38..c1d352009 100644 --- a/frontend/src/app/NotificationBanner.js +++ b/frontend/src/app/NotificationBanner.js @@ -1,24 +1,145 @@ -import React from 'react' -import { Flex, Box } from '@chakra-ui/react' -import { any } from 'prop-types' +import React, { useEffect, useState } from 'react' +import { Flex, Box, CloseButton, Button, Alert, AlertIcon, useToast } from '@chakra-ui/react' + +import { any, bool, oneOf, string } from 'prop-types' +import { CloseIcon } from '@chakra-ui/icons' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" +import { useMutation } from '@apollo/client' +import { DISMISS_MESSAGE } from '../graphql/mutations' +import { useUserVar } from '../utilities/userState' + +export function NotificationBanner({ children, hideable = false, bannerId, status = 'info', ...props }) { + const toast = useToast() + const { login, currentUser, isLoggedIn } = useUserVar() + + function checkHideBanner({ dismissedMessages }) { + const dismissedMessage = dismissedMessages.find((message) => message.messageId === bannerId) + return !!dismissedMessage + } + + const [hideBanner, setHideBanner] = useState( + checkHideBanner({ dismissedMessages: currentUser?.dismissedMessages || [], userLoggedIn: isLoggedIn() }), + ) + + const [dismissMessage, { loading, _error }] = useMutation(DISMISS_MESSAGE, { + onError: ({ message }) => { + toast({ + title: t`An error occurred when dismissing the message.`, + description: message, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + }, + onCompleted({ dismissMessage }) { + if (dismissMessage.result.__typename === 'DismissMessageResult') { + toast({ + title: t`Message dismissed successfully`, + description: t`The message has been dismissed and will not be shown again.`, + status: 'success', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + login({ + ...currentUser, + dismissedMessages: dismissMessage.result.user.dismissedMessages, + }) + } else if (dismissMessage.result.__typename === 'DismissMessageError') { + toast({ + title: t`Unable to dismiss the message.`, + description: dismissMessage.result.description, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else { + toast({ + title: t`Incorrect dismiss method received.`, + description: t`Incorrect dismissMessage.result typename.`, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } + }, + }) + + useEffect(() => { + const shouldHideBanner = checkHideBanner({ + dismissedMessages: currentUser?.dismissedMessages || [], + userLoggedIn: isLoggedIn(), + }) + + if (shouldHideBanner !== hideBanner) { + setHideBanner(shouldHideBanner) + } + }, [currentUser?.dismissedMessages]) + + const handleClose = () => { + setHideBanner(true) + } + + const handleDontShowAgain = async () => { + await dismissMessage({ + variables: { + messageId: bannerId, + }, + }) + } + + if (hideBanner) { + return null + } -export function NotificationBanner({ children, ...props }) { return ( - - - {children} - + + + + + + {children} + + {hideable && ( + + {bannerId && isLoggedIn() && ( + + )} + + + + + )} + + ) } NotificationBanner.propTypes = { + hideable: bool, + bannerId: string, + status: oneOf(['info', 'warning', 'success', 'error', 'loading']), children: any, } diff --git a/frontend/src/app/Page.js b/frontend/src/app/Page.js index cbccb5c73..6ba517c0d 100644 --- a/frontend/src/app/Page.js +++ b/frontend/src/app/Page.js @@ -1,16 +1,16 @@ import React from 'react' -import { Route } from 'react-router-dom' -import { bool, string } from 'prop-types' - +import { any, bool, string } from 'prop-types' import { useDocumentTitle } from '../utilities/useDocumentTitle' +import { ErrorBoundary } from 'react-error-boundary' +import { ErrorFallbackMessage } from '../components/ErrorFallbackMessage' -export const Page = ({ title, setTitle, ...props }) => { +export const Page = ({ title, setTitle, children }) => { useDocumentTitle(title, setTitle) - - return + return {children} } Page.propTypes = { title: string, setTitle: bool, + children: any, } diff --git a/frontend/src/app/PageNotFound.js b/frontend/src/app/PageNotFound.js index 33a6f62a1..36b597cbd 100644 --- a/frontend/src/app/PageNotFound.js +++ b/frontend/src/app/PageNotFound.js @@ -1,7 +1,7 @@ import React from 'react' import { Box, Divider, Heading, Stack, Text } from '@chakra-ui/react' import { WarningTwoIcon } from '@chakra-ui/icons' -import { Trans } from '@lingui/macro' +import { Trans } from "@lingui/react/macro" export default function PageNotFound() { return ( diff --git a/frontend/src/app/PrivatePage.js b/frontend/src/app/PrivatePage.js index dea1d9ce9..6c375efad 100644 --- a/frontend/src/app/PrivatePage.js +++ b/frontend/src/app/PrivatePage.js @@ -1,38 +1,19 @@ import React from 'react' import { bool, func } from 'prop-types' -import { Redirect, useLocation } from 'react-router-dom' - +import { Navigate, useLocation } from 'react-router-dom' import { Page } from './Page' -import { useUserVar } from '../utilities/userState' - // A wrapper for that redirects to the login // screen if you're not yet authenticated. -export function PrivatePage({ children, isLoginRequired, ...rest }) { - const { isLoggedIn, isEmailValidated } = useUserVar() +export function PrivatePage({ children, condition, ...rest }) { const location = useLocation() - return ( - - (isLoggedIn() && isEmailValidated()) || !isLoginRequired ? ( - children(props) - ) : ( - - ) - } - /> - ) + return {condition ? children : } } PrivatePage.propTypes = { children: func, isLoginRequired: bool, + condition: bool, ...Page.propTypes, } diff --git a/frontend/src/app/ReadGuidancePage.js b/frontend/src/app/ReadGuidancePage.js index 03a35da66..08f813712 100644 --- a/frontend/src/app/ReadGuidancePage.js +++ b/frontend/src/app/ReadGuidancePage.js @@ -1,15 +1,6 @@ import React from 'react' -import { Trans } from '@lingui/macro' -import { - Box, - Heading, - Text, - Link, - ListItem, - OrderedList, - UnorderedList, - Divider, -} from '@chakra-ui/react' +import { Trans } from "@lingui/react/macro" +import { Box, Heading, Text, Link, ListItem, OrderedList, UnorderedList, Divider, Code } from '@chakra-ui/react' import { useLingui } from '@lingui/react' export default function ReadGuidancePage() { @@ -35,9 +26,8 @@ export default function ReadGuidancePage() { > Directive on Service and Digital {' '} - provides expectations on how GC organizations are to manage their - Information Technology (IT) services. The focus of the Tracker tool - is to help organizations stay in compliance with the directives{' '} + provides expectations on how GC organizations are to manage their Information Technology (IT) services. The + focus of the Tracker tool is to help organizations stay in compliance with the directives{' '}
- - Below are steps on how government organizations can leverage the - Tracker platform: - + Below are steps on how government organizations can leverage the Tracker platform: {/* 1 */} @@ -78,20 +65,15 @@ export default function ReadGuidancePage() { - Identify any current affiliated Tracker users within your - organization and develop a plan with them. + Identify any current affiliated Tracker users within your organization and develop a plan with them. - If your organization has no affiliated users within Tracker, - contact the{' '} - + If your organization has no affiliated users within Tracker, contact the{' '} + TBS Cyber Security {' '} to assist in onboarding. @@ -101,9 +83,8 @@ export default function ReadGuidancePage() { - Once access is given to your department by the TBS Cyber - team, they will be able to invite and manage other users - within the organization and manage the domain list. + Once access is given to your department by the TBS Cyber team, they will be able to invite and + manage other users within the organization and manage the domain list. @@ -120,43 +101,26 @@ export default function ReadGuidancePage() { - Each organization’s domain list should include every - internet-facing service. It is the responsibility of - organization admins to manage the current list and identify - new domains to add. - - - - - - - To receive DKIM scan results and guidance, you must add the - DKIM selectors used for each domain. Organization - administrators can add selectors in the “Admin Profile” by - clicking the edit button of the domain for which they wish - to add the selector. Common selectors to keep an for are - “selector1”, and “selector2”. + Each organization’s domain list should include every internet-facing service. It is the + responsibility of organization admins to manage the current list and identify new domains to add. - Domains are only to be removed from your list when 1) they - no longer exist, meaning they are deleted from the DNS - returning an error code of NX DOMAIN (domain name does not - exist); or 2) if you have identified that they do not belong - to your organization. + Domains are only to be removed from your list when 1) they no longer exist, meaning they are deleted + from the DNS returning an error code of NXDOMAIN (domain name does not exist); or 2) if you have + identified that they do not belong to your organization. - If a domain is no longer in use but still exists on the - DNS, it is still vulnerable to email spoofing attacks, - where an attacker can send an email that appears to be - coming from your domain. + If a domain is no longer in use but still exists on the DNS, it is still vulnerable to email + spoofing attacks, where an attacker can send an email that appears to be coming from your + domain. @@ -172,21 +136,15 @@ export default function ReadGuidancePage() { - - The summary cards show two metrics that Tracker scans: - + The summary cards show two metrics that Tracker scans: - - The percentage of web-hosting services that strongly - enforce HTTPS - + The percentage of web-hosting services that strongly enforce HTTPS - The percentage of internet-facing services that have a - DMARC policy of at least p=”none” + The percentage of internet-facing services that have a DMARC policy of at least p=”none” @@ -194,10 +152,8 @@ export default function ReadGuidancePage() { - These metrics are an important first step in securing your - services and should be treated as minimum requirements. - Further metrics are available in your organization's domain - list. + These metrics are an important first step in securing your services and should be treated as minimum + requirements. Further metrics are available in your organization's domain list. @@ -208,45 +164,56 @@ export default function ReadGuidancePage() { {/* 4 */} - - Develop a prioritized schedule to address any failings: - + Develop a prioritized schedule to address any failings: - - Consider prioritizing websites and web services that exchange - Protected data. - + Consider prioritizing websites and web services that exchange Protected data. - - Where necessary adjust IT Plans and budget estimates where - work is expected. - + Where necessary adjust IT Plans and budget estimates where work is expected. - It is recommended that Shared Service Canada (SSC) partners - contact their SSC Service Delivery Manager to discuss action - plans and required steps to submit a request for change. + It is recommended that Shared Service Canada (SSC) partners contact their SSC Service Delivery Manager + to discuss action plans and required steps to submit a request for change. - Obtain certificates from a GC-approved certificate source as - outlined in the Recommendations for TLS Server Certificates - for GC public facing web services + Obtain certificates from a GC-approved certificate source as outlined in the Recommendations for TLS + Server Certificates for GC public facing web services - Obtain the configuration guidance for the appropriate - endpoints (e.g., web server, network/security appliances, - etc.) and implement recommended configurations. + Obtain the configuration guidance for the appropriate endpoints (e.g., web server, network/security + appliances, etc.) and implement recommended configurations. + {/* 5 */} + + + + To ensure accurate scanning results, configure your firewall and DDoS (Denial of Service) protection + settings to permit required scanning traffic. Work with your IT team and/or your SSC Service Delivery + Manager to add the scanning IP address (52.138.13.28) to your network's allow lists. + + + + + + Our scan requests can be identified by the following User-Agent header:{' '} + + Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0 Tracker-Suivi + (+https://github.com/canada-ca/tracker) + + + + + + {/* 6 */} Links to Review: @@ -256,18 +223,12 @@ export default function ReadGuidancePage() { Tracker: - + Wiki - + List of guidance tags @@ -291,8 +252,7 @@ export default function ReadGuidancePage() { color="blue.500" isExternal > - Web Sites and Services Management Configuration - Requirements + Web Sites and Services Management Configuration Requirements @@ -310,8 +270,7 @@ export default function ReadGuidancePage() { color="blue.500" isExternal > - Guidance on securely configuring network protocols - (ITSP.40.062) + Guidance on securely configuring network protocols (ITSP.40.062)
@@ -354,8 +313,7 @@ export default function ReadGuidancePage() { color="blue.500" isExternal > - Implementation guidance: email domain protection - (ITSP.40.065 v1.1) + Implementation guidance: email domain protection (ITSP.40.065 v1.1) @@ -367,7 +325,7 @@ export default function ReadGuidancePage() { - + Frequently Asked Questions @@ -380,10 +338,7 @@ export default function ReadGuidancePage() { Please contact{' '} - + TBS Cyber Security {' '} for help. @@ -396,52 +351,29 @@ export default function ReadGuidancePage() { How can I edit my domain list? - - Admins of an organization can add domains to their list. - - - - - Requests for updates can be sent directly to{' '} - - TBS Cyber Security - - . - + Admins of an organization can add domains to their list. Only{' '} - + TBS Cyber Security {' '} - can remove domains from your organization. Domains are only to - be removed from your list when 1) they no longer exist, - meaning they are deleted from the DNS returning an error code - of NX DOMAIN (domain name does not exist); or 2) if you have - identified that they do not belong to your organization. + can remove domains from your organization. Domains are only to be removed from your list when 1) they + no longer exist, meaning they are deleted from the DNS returning an error code of NX DOMAIN (domain + name does not exist); or 2) if you have identified that they do not belong to your organization. {/* 3 */} - - Why do other tools show positive results for a domain while - Tracker shows negative results? - + Why do other tools show positive results for a domain while Tracker shows negative results? - While other tools are useful to work alongside Tracker, they - do not specifically adhere to the configuration requirements - specified in the{' '} + While other tools are useful to work alongside Tracker, they do not specifically adhere to the + configuration requirements specified in the{' '} Web Site and Service Management Configuration Requirements - . For a list of allowed protocols, ciphers, and curves review - the{' '} + . For a list of allowed protocols, ciphers, and curves review the{' '} - By default our scanners check domains ending in “.gc.ca” and - “.canada.ca”. If your domain is outside that set, you need to - contact us to let us know. Send an email to{' '} - + By default our scanners check domains ending in “.gc.ca” and “.canada.ca”. If your domain is outside + that set, you need to contact us to let us know. Send an email to{' '} + TBS Cyber Security {' '} to confirm your ownership of that domain. - - Another possibility is that your domain is not internet - facing. - + Another possibility is that your domain is not internet facing. @@ -535,22 +459,56 @@ export default function ReadGuidancePage() {
- - - Why does the guidance page not show the domain’s DKIM selectors - even though they exist? - + {/* 6 */} + + Why does the guidance page not show the domain’s DKIM selectors even though they exist? - Tracker does not automatically add selectors, so it is likely - that they are not in the system yet. More information can be - found above in Getting Started. + Tracker automatically adds DKIM selectors using DMARC reports. Selectors will be added to Tracker when + 1) the domain has a DMARC RUA record which includes "mailto:dmarc@cyber.gc.ca"; and 2) the selector + has been used to sign an email and passed DKIM validation. If your DKIM selectors or any DMARC + information is missing, please email{' '} + + TBS Cyber Security + + . + + + + + The process of detecting DKIM selectors is not immediate. It may take more than 24 hours for the + selectors to appear in Tracker after the conditions are met. {/* 7 */} + + + My domain does not send emails, how can I get my domain's DMARC, DKIM, and SPF compliance checks to pass? + + + + + Follow the guidance found in section B.4 of the{' '} + + ITSP.40.065 v1.1 + + . + + + + + {/* 8 */} References: @@ -563,10 +521,7 @@ export default function ReadGuidancePage() { } color="blue.500" > - - Domain Name System (DNS) Services Management Configuration - Requirements - Canada.ca - + Domain Name System (DNS) Services Management Configuration Requirements - Canada.ca @@ -578,10 +533,7 @@ export default function ReadGuidancePage() { } color="blue.500" > - - Email Management Services Configuration Requirements - - Canada.ca - + Email Management Services Configuration Requirements - Canada.ca @@ -594,8 +546,8 @@ export default function ReadGuidancePage() { color="blue.500" > - Implementation guidance: email domain protection - (ITSP.40.065 v1.1) - Canadian Centre for Cyber Security + Implementation guidance: email domain protection (ITSP.40.065 v1.1) - Canadian Centre for Cyber + Security @@ -605,19 +557,39 @@ export default function ReadGuidancePage() { - - - Protect domains that do not send email - GOV.UK (www.gov.uk) - + + Protect domains that do not send email - GOV.UK (www.gov.uk) + + Service Level Agreement + + + + + + Help Desk: All enquiries submitted via generic mailboxes{' '} + + tracker@tbs-sct.gc.ca + {' '} + will be responded to within 10 business days. + + + + + Application Availability: This cloud-based web application is to be 95% fully operational from 8:00 + AM to 4:00 PM Eastern time on regular business days. + + + + For any questions or concerns, please contact{' '} diff --git a/frontend/src/app/RequestScanNotificationHandler.js b/frontend/src/app/RequestScanNotificationHandler.js index 93c753e56..736d2baa1 100644 --- a/frontend/src/app/RequestScanNotificationHandler.js +++ b/frontend/src/app/RequestScanNotificationHandler.js @@ -1,7 +1,7 @@ import React from 'react' import { useSubscription } from '@apollo/client' import { Box, useToast } from '@chakra-ui/react' -import { t } from '@lingui/macro' +import { t } from "@lingui/core/macro" import { node } from 'prop-types' import { useUserVar } from '../utilities/userState' diff --git a/frontend/src/app/ScrollToAnchor.js b/frontend/src/app/ScrollToAnchor.js new file mode 100644 index 000000000..ab7e3051c --- /dev/null +++ b/frontend/src/app/ScrollToAnchor.js @@ -0,0 +1,109 @@ +import { useLocation } from 'react-router-dom' +import { useEffect, useRef, useState } from 'react' + +function isInteractiveElement(element) { + const formTags = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'] + const linkTags = ['A', 'AREA'] + return ( + (formTags.includes(element.tagName) && !element.hasAttribute('disabled')) || + (linkTags.includes(element.tagName) && element.hasAttribute('href')) + ) +} + +function scrollToElement(element) { + const originalTabIndex = element.getAttribute('tabindex') + if ([null, 'none'].includes(originalTabIndex) && !isInteractiveElement(element)) { + element.setAttribute('tabindex', -1) + } + + const originalOutline = getComputedStyle(element).outline + const originalBoxShadow = getComputedStyle(element).boxShadow + if (!isInteractiveElement(element)) { + element.style.boxShadow = 'var(--chakra-shadows-outline)' + // set focus outline to none if the element is not interactive to + // disable the default focus outline (which only appears on some events) + element.style.outline = 'none' + } + + element.addEventListener('blur', () => { + if (!isInteractiveElement(element)) { + if (originalTabIndex === null) element.removeAttribute('tabindex') + element.style.boxShadow = originalBoxShadow + element.style.outline = originalOutline + } + }) + element.focus({ preventScroll: true }) + + // scroll inside setTimeout to prevent scrollIntoView from being ignored + // observed in Brave browser where focus's preventScroll prevents scrollIntoView + setTimeout(() => { + const preferReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches + element.scrollIntoView({ behavior: preferReducedMotion ? 'auto' : 'smooth', block: 'start' }) + }, 1) + + return true +} + +export function ScrollToAnchor() { + const location = useLocation() + const lastHash = useRef('') + const [observer, setObserver] = useState(null) + const timer = useRef(null) + + // listen to location change using useEffect with location as dependency + // https://jasonwatmore.com/react-router-v6-listen-to-location-route-change-without-history-listen + useEffect(() => { + if (observer !== null) { + observer.disconnect() + setObserver(null) + } + + if (location.hash) { + lastHash.current = location.hash.slice(1) + } else { + lastHash.current = '' + } + + if (lastHash.current) { + const el = document.getElementById(lastHash.current) + + if (el) { + scrollToElement(el) + } else { + const observer = new MutationObserver((_mutationsList, observer) => { + const el = document.getElementById(lastHash.current) + if (el) { + clearTimeout(timer.current) + timer.current = null + observer.disconnect() + setObserver(null) + scrollToElement(el) + } + }) + + setObserver(observer) + observer.observe(document, { + attributes: true, + childList: true, + subtree: true, + }) + + timer.current = setTimeout(() => { + if (observer !== null) { + observer.disconnect() + setObserver(null) + } + }, 5000) + } + } + + return () => { + if (observer !== null) { + observer.disconnect() + setObserver(null) + } + } + }, [location]) + + return null +} diff --git a/frontend/src/app/SlideMessage.js b/frontend/src/app/SlideMessage.js index 77fc6cc0d..36de7aa7f 100644 --- a/frontend/src/app/SlideMessage.js +++ b/frontend/src/app/SlideMessage.js @@ -1,7 +1,7 @@ import React from 'react' import { useLingui } from '@lingui/react' -import { Trans } from '@lingui/macro' +import { Trans } from "@lingui/react/macro" import sigEn from '../images/goc-header-logo-en.svg' import sigFr from '../images/goc-header-logo-fr.svg' diff --git a/frontend/src/app/TopBanner.js b/frontend/src/app/TopBanner.js index fc6fdf460..2df0ea448 100644 --- a/frontend/src/app/TopBanner.js +++ b/frontend/src/app/TopBanner.js @@ -1,6 +1,7 @@ import React from 'react' -import { t, Trans } from '@lingui/macro' -import { Box, Button, Flex, useToast, Image, Link } from '@chakra-ui/react' +import { t } from "@lingui/core/macro" +import { Trans } from "@lingui/react/macro" +import { Box, Button, Flex, useToast, Image, Link, Skeleton } from '@chakra-ui/react' import { Link as RouteLink } from 'react-router-dom' import { useMutation } from '@apollo/client' @@ -17,8 +18,10 @@ import { SIGN_OUT } from '../graphql/mutations' import { PhaseBanner } from './PhaseBanner' import { useLingui } from '@lingui/react' import { ABTestWrapper, ABTestVariant } from './ABTestWrapper' +import { bool } from 'prop-types' +import { TourButton } from '../userOnboarding/components/TourButton' -export const TopBanner = (props) => { +export const TopBanner = ({ initialLoading, ...props }) => { const { isLoggedIn, logout } = useUserVar() const toast = useToast() const { i18n } = useLingui() @@ -94,42 +97,58 @@ export const TopBanner = (props) => { - - - - - {isLoggedIn() ? ( - - ) : ( + + + {initialLoading ? ( + <> + + + + + + + + ) : isLoggedIn() ? ( <> + + ) : ( + <> + {window.env?.APP_IS_PRODUCTION === true && ( + + )} )} @@ -137,3 +156,7 @@ export const TopBanner = (props) => { ) } + +TopBanner.propTypes = { + initialLoading: bool, +} diff --git a/frontend/src/app/__tests__/App.test.js b/frontend/src/app/__tests__/App.test.js index 063c1df6c..def4e881d 100644 --- a/frontend/src/app/__tests__/App.test.js +++ b/frontend/src/app/__tests__/App.test.js @@ -4,25 +4,13 @@ import { MemoryRouter } from 'react-router-dom' import { cleanup, render, waitFor } from '@testing-library/react' import { MockedProvider } from '@apollo/client/testing' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' - import { App } from '../App' - import { UserVarProvider } from '../../utilities/userState' import { REFRESH_TOKENS } from '../../graphql/mutations' import { IS_LOGIN_REQUIRED } from '../../graphql/queries' - -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) +import { TourProvider } from '../../userOnboarding/contexts/TourContext' const mocks = [ { @@ -72,7 +60,9 @@ describe('', () => { - + + + @@ -97,7 +87,9 @@ describe('', () => { - + + + @@ -126,7 +118,9 @@ describe('', () => { - + + + @@ -135,15 +129,13 @@ describe('', () => { ) expect( - queryByText( - /To enable full app functionality and maximize your account's security/, - ), + queryByText(/To enable full app functionality and maximize your account's security/), ).not.toBeInTheDocument() }) }) describe('user is logged in', () => { - it('renders when user not verified', () => { + it('renders when user not verified', async () => { const { getByText } = render( ', () => { - + + + , ) - expect( - getByText( - /To enable full app functionality and maximize your account's security/, - ), - ).toBeInTheDocument() + await waitFor(() => + expect(getByText(/To enable full app functionality and maximize your account's security/)).toBeInTheDocument(), + ) }) it('does not render if user is verified', () => { const { queryByText } = render( @@ -182,7 +174,9 @@ describe('', () => { - + + + @@ -190,15 +184,13 @@ describe('', () => { , ) expect( - queryByText( - /To enable full app functionality and maximize your account's security/, - ), + queryByText(/To enable full app functionality and maximize your account's security/), ).not.toBeInTheDocument() }) }) describe('When login is not required', () => { - it('displays additional navbar options', () => { + it('displays additional navbar options', async () => { const { queryByRole } = render( ', () => { - + + + , ) - expect( - queryByRole('link', { name: 'Organizations', hidden: false }), - ).toBeInTheDocument() + await waitFor(() => expect(queryByRole('link', { name: 'Organizations', hidden: false })).toBeInTheDocument()) }) }) }) diff --git a/frontend/src/app/__tests__/ContactUsPage.test.js b/frontend/src/app/__tests__/ContactUsPage.test.js index 01bda9554..76ff46c91 100644 --- a/frontend/src/app/__tests__/ContactUsPage.test.js +++ b/frontend/src/app/__tests__/ContactUsPage.test.js @@ -4,24 +4,11 @@ import { MemoryRouter } from 'react-router-dom' import { cleanup, fireEvent, render, waitFor } from '@testing-library/react' import { MockedProvider } from '@apollo/client/testing' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' +import { i18n } from '@lingui/core' import { makeVar } from '@apollo/client' -import { en } from 'make-plural/plurals' - import ContactUsPage from '../ContactUsPage' - import { UserVarProvider } from '../../utilities/userState' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: { plurals: en }, - }, -}) - describe('', () => { afterEach(cleanup) @@ -68,9 +55,7 @@ describe('', () => { , ) - const button = await waitFor(() => - getByRole('link', { name: /Contact Us/i }), - ) + const button = await waitFor(() => getByRole('link', { name: /Contact Us/i })) await waitFor(() => { expect(button) fireEvent.click(button) diff --git a/frontend/src/app/__tests__/FloatingMenu.test.js b/frontend/src/app/__tests__/FloatingMenu.test.js index 2a47cf5fb..2339d3eaa 100644 --- a/frontend/src/app/__tests__/FloatingMenu.test.js +++ b/frontend/src/app/__tests__/FloatingMenu.test.js @@ -2,34 +2,20 @@ import React from 'react' import { cleanup, render, waitFor } from '@testing-library/react' import { theme, ChakraProvider } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' -import { MemoryRouter, Route } from 'react-router-dom' +import { i18n } from '@lingui/core' +import { createMemoryRouter, MemoryRouter, RouterProvider } from 'react-router-dom' import { fireEvent } from '@testing-library/dom' import { MockedProvider } from '@apollo/client/testing' import { makeVar } from '@apollo/client' - import { FloatingMenu } from '../FloatingMenu' - import { UserVarProvider } from '../../utilities/userState' import { SIGN_OUT } from '../../graphql/mutations' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: {}, - }, -}) - describe('', () => { it('renders', async () => { const { getByText } = render( - + @@ -109,41 +95,41 @@ describe('', () => { }) }) }) - }) - - describe('when the menu is open', () => { describe("and the 'Sign In' button is clicked", () => { it('redirects to the sign in page', async () => { - let wLocation + const router = createMemoryRouter( + [ + { + path: '/sign-in', + element:
Sign in
, + }, + { + path: '/', + element: , + }, + ], + { + initialEntries: ['/'], + initialIndex: 0, + }, + ) const { getByText } = render( - - - - - - { - wLocation = location - return null - }} - /> - - - + + + + + + , ) - const menuButton = getByText(/Menu/i) - fireEvent.click(menuButton) + + await waitFor(() => { + const menuButton = getByText(/Menu/i) + fireEvent.click(menuButton) + }) await waitFor(() => { expect(getByText(/Sign In/i)).toBeInTheDocument() @@ -153,7 +139,7 @@ describe('', () => { fireEvent.click(signInButton) await waitFor(() => { - expect(wLocation.pathname).toBe('/sign-in') + expect(router.state.location.pathname).toBe('/sign-in') }) }) }) @@ -253,9 +239,7 @@ describe('', () => { fireEvent.click(signOutButton) await waitFor(() => { - expect( - queryByText(/An error occured when you attempted to sign out/i), - ) + expect(queryByText(/An error occured when you attempted to sign out/i)) }) }) }) diff --git a/frontend/src/app/__tests__/FloatingMenuLink.test.js b/frontend/src/app/__tests__/FloatingMenuLink.test.js index 7a6d8f3f1..e074f34be 100644 --- a/frontend/src/app/__tests__/FloatingMenuLink.test.js +++ b/frontend/src/app/__tests__/FloatingMenuLink.test.js @@ -1,34 +1,19 @@ import React from 'react' -import { render, waitFor } from '@testing-library/react' +import { render, waitFor, fireEvent } from '@testing-library/react' import { theme, ChakraProvider } from '@chakra-ui/react' import { I18nProvider } from '@lingui/react' -import { setupI18n } from '@lingui/core' -import { MemoryRouter, Route } from 'react-router-dom' -import { fireEvent } from '@testing-library/dom' +import { i18n } from '@lingui/core' +import { createMemoryRouter, MemoryRouter, RouterProvider } from 'react-router-dom' import { MockedProvider } from '@apollo/client/testing' import { makeVar } from '@apollo/client' - import { FloatingMenuLink } from '../FloatingMenuLink' - import { UserVarProvider } from '../../utilities/userState' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: {}, - }, -}) - describe('', () => { it('renders', async () => { const { getByText } = render( - + @@ -44,31 +29,31 @@ describe('', () => { describe('when the link is clicked', () => { it('redirects', async () => { - let wLocation + const router = createMemoryRouter( + [ + { + path: '/sign-in', + element:
Sign in page
, + }, + { + path: '/', + element: , + }, + ], + { + initialEntries: ['/'], + initialIndex: 0, + }, + ) const { getByText } = render( - - - - - - { - wLocation = location - return null - }} - /> - - - + + + + + + , ) @@ -76,7 +61,7 @@ describe('', () => { fireEvent.click(signInLink) await waitFor(() => { - expect(wLocation.pathname).toBe('/sign-in') + expect(router.state.location.pathname).toBe('/sign-in') }) }) }) diff --git a/frontend/src/app/__tests__/Footer.test.js b/frontend/src/app/__tests__/Footer.test.js index 84f13808b..435ebc568 100644 --- a/frontend/src/app/__tests__/Footer.test.js +++ b/frontend/src/app/__tests__/Footer.test.js @@ -2,20 +2,9 @@ import React from 'react' import { I18nProvider } from '@lingui/react' import { theme, ChakraProvider } from '@chakra-ui/react' import { render } from '@testing-library/react' -import { setupI18n } from '@lingui/core' - +import { i18n } from '@lingui/core' import { Footer } from '../Footer' -const i18n = setupI18n({ - locale: 'en', - messages: { - en: {}, - }, - localeData: { - en: {}, - }, -}) - describe('