diff --git a/.github/workflows/docker-scout.yml b/.github/workflows/docker-scout.yml index 158daf0ebb..143244bc12 100644 --- a/.github/workflows/docker-scout.yml +++ b/.github/workflows/docker-scout.yml @@ -6,7 +6,6 @@ on: - 'main' - 'docker-scout' paths: - - 'yarn.lock' - 'services/api/**' - '.github/workflows/docker-scout.yml' pull_request: @@ -14,7 +13,6 @@ on: - 'main' - 'docker-scout' paths: - - 'yarn.lock' - 'services/api/**' - '.github/workflows/docker-scout.yml' diff --git a/.github/workflows/test-db-migrations.yml b/.github/workflows/test-db-migrations.yml index f96892cad7..d99c67c878 100644 --- a/.github/workflows/test-db-migrations.yml +++ b/.github/workflows/test-db-migrations.yml @@ -52,11 +52,11 @@ jobs: - name: Initiate rollback run: | - docker compose -p lagoon exec api sh -c './node_modules/.bin/knex migrate:rollback --all --cwd /app/services/api/database' + docker compose -p lagoon exec api sh -c './node_modules/.bin/knex migrate:rollback --all --cwd /app/database' - name: Reperform initial migration run: | - docker compose -p lagoon exec api sh -c './node_modules/.bin/knex migrate:latest --cwd /app/services/api/database' + docker compose -p lagoon exec api sh -c './node_modules/.bin/knex migrate:latest --cwd /app/database' - name: Remove testing setup run: | diff --git a/.gitignore b/.gitignore index 4ec171c5f5..85ee13dbf8 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ calico.yaml docker-compose.override.yml scan.txt build.txt +services/**/.pnpm-store diff --git a/.vscode/launch.json b/.vscode/launch.json index 934c93dbf6..f87f8c97e9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,9 +10,9 @@ "request": "attach", "port": 9229, "address": "localhost", - "outFiles": ["${workspaceRoot}/app/services/api/dist/**/*.js"], + "outFiles": ["${workspaceRoot}/services/api/dist/**/*.js"], "localRoot": "${workspaceFolder}/services/api", - "remoteRoot": "/app/services/api", + "remoteRoot": "/app", "sourceMaps": true, "protocol": "inspector" } diff --git a/.vscode/settings.json b/.vscode/settings.json index ce9d275d01..d84481e7e0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,7 @@ // Place your settings in this file to overwrite default and user settings. { "eslint.workingDirectories": [ - "./node-packages/commons", - "./services/auth-server" + "./services/api" ], "git.ignoreLimitWarning": true, "javascript.validate.enable": false, diff --git a/Makefile b/Makefile index 7378ef1eef..98e1d59f98 100644 --- a/Makefile +++ b/Makefile @@ -165,16 +165,7 @@ docker_pull: ####### Services Images are the Docker Images used to run the Lagoon Microservices, these images ####### will be expected by docker compose to exist. -# Yarn Workspace Image which builds the Yarn Workspace within a single image. This image will be -# used by all microservices based on Node.js to not build similar node packages again -build-images += yarn-workspace-builder -build/yarn-workspace-builder: yarn-workspace-builder/Dockerfile - $(eval image = $(subst build/,,$@)) - $(call docker_buildx_create) - $(call docker_build,$(image),$(image)/Dockerfile,.) - $(call scan_image,$(image),) - -####### +###### ####### Task Images ####### ####### Task Images are standalone images that are used to run advanced tasks when using the builddeploy controllers. @@ -224,7 +215,7 @@ $(build-services): $(call scan_image,$(image),) # Dependencies of Service Images -build/auth-server build/webhook-handler build/api: build/yarn-workspace-builder +build/api: services/api/Dockerfile build/api-db: services/api-db/$(DATABASE_DOCKERFILE) build/api-redis: services/api-redis/Dockerfile build/actions-handler: services/actions-handler/Dockerfile @@ -402,20 +393,19 @@ down: kill: docker ps --format "{{.Names}}" | grep lagoon | xargs -t -r -n1 docker rm -f -v -.PHONY: local-dev-yarn -local-dev-yarn: - $(MAKE) local-dev-yarn-stop - docker run --name local-dev-yarn -d -v ${PWD}:/app uselagoon/node-22-builder - docker exec local-dev-yarn bash -c "yarn install --frozen-lockfile" - docker exec local-dev-yarn bash -c "cd /app/node-packages/commons && yarn build" - echo -e "use 'yarn workspace api add package@version' to update a package in workspace api" - docker exec -it local-dev-yarn bash - $(MAKE) local-dev-yarn-stop - -.PHONY: local-dev-yarn-stop -local-dev-yarn-stop: - docker stop local-dev-yarn || true - docker rm local-dev-yarn || true +.PHONY: local-dev-pnpm +local-dev-pnpm: + $(MAKE) local-dev-pnpm-stop + docker run --name local-dev-pnpm -d -v ${PWD}/services/api:/app uselagoon/node-22-builder + docker exec local-dev-pnpm bash -c "corepack enable && pnpm install --frozen-lockfile --force" + echo -e "use 'pnpm add package@version' to update a package in the api" + docker exec -it local-dev-pnpm bash + $(MAKE) local-dev-pnpm-stop + +.PHONY: local-dev-pnpm-stop +local-dev-pnpm-stop: + docker stop local-dev-pnpm || true + docker rm local-dev-pnpm || true .PHONY: ui-development ui-development: build-ui-logs-development @@ -906,7 +896,7 @@ k3d/local-dev-patch: export IMAGE_REGISTRY="registry.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io/library" \ && for image in $(LOCAL_DEV_SERVICES); do \ echo "building $$image" \ - && cd services/$$image && yarn install && yarn build && cd ../..; \ + && cd services/$$image && corepack enable && pnpm install && pnpm build && cd ../..; \ done \ && for image in $(LOCAL_DEV_SERVICES); do \ echo "patching lagoon-core-$$image" \ diff --git a/STANDALONE_SERVICES_PLAN.md b/STANDALONE_SERVICES_PLAN.md new file mode 100644 index 0000000000..cc43b04857 --- /dev/null +++ b/STANDALONE_SERVICES_PLAN.md @@ -0,0 +1,254 @@ +# Yarn-to-pnpm Standalone Services Migration Plan + +> Reverse-engineered from the `testing/yarn-to-pnpm` branch (forked from `webhook-handler-go`). +> Each phase below corresponds to a logical group of commits that were typically landed as a single unit (sometimes with follow-up fixups). + +--- + +## Background + +The Lagoon monorepo historically used **Yarn Workspaces** to link Node.js services +(`api`, `auth-server`) and a shared `node-packages/commons` library together. A root +`package.json`, root `yarn.lock`, root `tsconfig.json`, and a `yarn-workspace-builder` +Docker stage formed the glue. + +Go services (`actions-handler`, `backup-handler`, `logs2notifications`, +`webhook-handler`, `api-sidecar-handler`) already operate as standalone modules +using the root `go.mod`. + +### Prerequisites (already complete) + +The following work was completed on the `webhook-handler-go` branch prior to this +migration and is assumed as a starting point: + +- **webhook-handler** rewritten from Node.js/TypeScript to Go +- **webhooks2tasks** eliminated — responsibilities absorbed into the Go + webhook-handler and api-sidecar-handler +- **backup-handler** refactored to talk to the API directly instead of the broker +- Old Node.js webhook-handler and webhooks2tasks removed from the Yarn workspace + +This means the only remaining Yarn workspace members are **`api`** and +**`auth-server`**, plus the shared **`node-packages/commons`** library. + +--- + +## Goal + +Make every Node.js service fully standalone — each service owns its own +dependencies, lockfile, and build — eliminating the shared Yarn workspace +infrastructure entirely, and migrating from Yarn to pnpm. + +--- + +## Phase 1: Auth-server standalone + pnpm migration + +**Commits:** `defd98e7b`..`141f6adde` (3 commits) +**Scope:** 12 files changed, +4,046 / −2,400 lines + +### What was done + +1. **Disconnected `auth-server`** from the Yarn workspace — gave it its own + `package.json` dependencies (previously relied on hoisted workspace deps). +2. **Migrated `auth-server` from Yarn to pnpm** — added `pnpm-lock.yaml`, + removed `yarn.lock`. +3. **Detached ESLint config** — `auth-server` got its own ESLint setup instead + of referencing the shared `eslint-config-lagoon-node` package from + `node-packages/`. +4. Updated `auth-server/Dockerfile` to use standalone pnpm install instead of + the `yarn-workspace-builder` multi-stage pattern. + +### Key decisions + +- `auth-server` is the simpler of the two remaining Node services, so it was + migrated first as a proof of concept. +- pnpm was chosen over Yarn for standalone services (faster, stricter dependency + resolution, better monorepo-to-standalone story). + +--- + +## Phase 2: Remove yarn-workspace-builder — standalone API build + +**Commit:** `fc426616f` +**Scope:** 4 files changed, +24 / −47 lines + +### What was done + +1. **Deleted `yarn-workspace-builder/Dockerfile`** — the Docker multi-stage builder + image that previously installed all Yarn workspace dependencies for every Node + service. +2. **Updated `services/api/Dockerfile`** to install its own dependencies directly + instead of copying from the `yarn-workspace-builder` stage. +3. **Cleaned up `Makefile` and `docker-bake.hcl`** — removed references to the + yarn-workspace-builder target. + +### Why this is separate + +The build change can be landed and tested in isolation. After this commit the API +still uses Yarn and still references `node-packages/commons` — but its Docker build +no longer depends on the shared builder image. + +--- + +## Phase 3: Consolidate commons and remove Yarn workspace root + +**Commits:** `d389edd91`..`831981965` (4 commits + fixups) +**Scope:** ~95 files changed + +### What was done + +1. **Consolidated `node-packages/commons` into `services/api/`** — all shared + TypeScript utilities, types, JWT helpers, logging, and API client code were + moved into the API service's source tree (under `src/commons/` or directly + into relevant modules). +2. **Updated all import paths** in the API codebase from `@lagoon/commons/...` + to local relative imports. +3. **Removed `node-packages/` directory entirely** — no more shared packages. +4. **Removed `node-packages/eslint-config-lagoon-node/`** — each service now + owns its own lint configuration. +5. **Deleted root `package.json`** — no more Yarn workspace root. +6. **Moved root `yarn.lock` into `services/api/`** — the API now owns its own + lockfile. +7. **Deleted root `tsconfig.json`** — each service has its own TypeScript config. +8. Fixed K3d config and Makefile UI override issues from the restructure. + +### Key decisions + +- Commons code was merged *into the API* rather than published as an npm package, + because the API was the only remaining consumer after webhook-handler and + webhooks2tasks were rewritten in Go. +- This eliminated the need for workspace linking entirely. + +--- + +## Phase 4: API — Yarn to pnpm migration + +**Commits:** `2a95edead`..`e526a9a8b` (7 commits) +**Scope:** 183 files changed, +21,251 / −19,968 lines + +### What was done + +1. **Updated API packages** — refreshed all dependencies to current versions. +2. **Migrated API from Yarn to pnpm** — added `pnpm-lock.yaml`, removed + `yarn.lock`. +3. Fixed missing packages surfaced by pnpm's stricter dependency resolution + (`graphql-tag`, Ramda imports, etc.). +4. **TypeScript fixups** — resolved type errors from stricter package versions. +5. **Fixed Jest configuration** — adapted test runner config for standalone pnpm + setup, added module mocks for `mariadb`, `node-fetch`, and `redis` so the + existing test suites run without external dependencies. + +### Key decisions + +- The bulk of the line changes are lockfile churn (`yarn.lock` deleted, + `pnpm-lock.yaml` added). +- pnpm's strict mode caught several implicit/phantom dependencies that Yarn's + hoisting had silently resolved. + +--- + +## Phase 5: API test coverage expansion + +**Commit:** `e526a9a8b` (initial), ongoing +**Reference:** `services/api/TEST_COVERAGE_PLAN.md` + +### Current state + +8 test suites, 22 tests, 14 snapshots — all passing. Coverage is concentrated in +SQL query builders and one permission helper. Utility code, business-logic helpers, +and the auth layer have no tests. + +### What was done + +1. **Fixed Jest for pnpm** — added `jest.config.js` with `moduleNameMapper` and + manual mocks (`__mocks__/mariadb.js`, `__mocks__/node-fetch.js`, + `__mocks__/redis.js`) so tests run without live infrastructure. +2. **Added `sshKey/helpers.ts`** — extracted `validateSshKey` into a testable + pure function. +3. **Updated snapshots** — regenerated environment and notification snapshots to + match the post-migration import paths. +4. **Created `TEST_COVERAGE_PLAN.md`** — a tiered plan for expanding API test + coverage (see below). + +### Planned test tiers + +| Tier | Target | Effort | Description | +|------|--------|--------|-------------| +| 1 | Pure utilities | Low | `func.ts`, `snakeCase.ts`, `pickNonNil.ts`, `convertDate.ts`, `advancedTaskDefinitionArgument.ts` — no DB, no mocks | +| 2 | SQL query builders | Low | Snapshot tests for all `sql.ts` files (`task`, `deployment`, `routes`, `backup`, `organization`, `problem`, etc.) — same pattern as existing tests | +| 3 | Business logic + regressions | Medium | `userActivityLogger` (regression), `extractWsToken` (auth fallback paths), `filterAdminTasks`, `groupByProblemIdentifier`, environment validators | +| 4 | Model / integration layer | High | `user.ts` model, `group.ts` model, `authMiddleware.ts` — requires mocked `sqlClientPool` and `keycloak-admin` | + +### Coverage targets + +| Category | Current | After Tier 1+2 | After Tier 3+4 | +|----------|---------|----------------|----------------| +| Utility functions | ~0% | ~90% | ~90% | +| SQL builders | ~20% | ~85% | ~85% | +| Business logic helpers | ~5% | ~30% | ~70% | +| Auth / models | ~5% | ~10% | ~50% | +| Resolvers | 0% | 0% | 0% (covered by E2E) | + +### Not unit tested (by design) + +Resolver files, server bootstrap, HTTP route handlers, and third-party client +wrappers are better covered by the existing k3d integration/E2E test suite. + +--- + +## Final State + +### Before (starting point — post `webhook-handler-go`) + +``` +lagoon/ +├── package.json # Yarn workspace root +├── yarn.lock # Shared lockfile +├── tsconfig.json # Shared TypeScript config +├── yarn-workspace-builder/ # Docker builder for all Node services +├── node-packages/ +│ ├── commons/ # Shared TS library (JWT, logging, API client, utils) +│ └── eslint-config-lagoon-node/ +└── services/ + ├── api/ # Node/TS — Yarn workspace member + ├── auth-server/ # Node/TS — Yarn workspace member + └── ... # Go services already standalone +``` + +### After (on `testing/yarn-to-pnpm`) + +``` +lagoon/ +└── services/ + ├── api/ # Node/TS — standalone, pnpm, owns former commons code + │ ├── package.json + │ ├── pnpm-lock.yaml + │ ├── tsconfig.json + │ └── src/commons/ # Absorbed from node-packages/commons + ├── auth-server/ # Node/TS — standalone, pnpm + │ ├── package.json + │ └── pnpm-lock.yaml + └── ... # Go services unchanged +``` + +### What was removed + +| Artifact | Reason | +|----------|--------| +| `package.json` (root) | No more Yarn workspaces | +| `yarn.lock` (root) | Each service has its own lockfile | +| `tsconfig.json` (root) | Each service has its own TS config | +| `yarn-workspace-builder/` | No shared Docker build stage needed | +| `node-packages/commons/` | Merged into `services/api/` | +| `node-packages/eslint-config-lagoon-node/` | Each service owns its lint config | + +--- + +## Remaining / Future Work + +- [ ] **CI/CD pipeline updates** — Jenkinsfile and docker-bake.hcl may need + adjustments for the new standalone build model (no more yarn-workspace-builder + stage). +- [ ] **Helm chart alignment** — charts need to reflect any changed image names or + build targets. +- [ ] **Integration test updates** — ensure the Ansible-based test suite works with + the standalone service builds. diff --git a/docker-bake.hcl b/docker-bake.hcl index 1ee9ca9eb1..62b13cb14a 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -147,18 +147,10 @@ group "prod-images" { ] } -target "yarn-workspace-builder" { - inherits = ["default"] - context = "." - dockerfile = "yarn-workspace-builder/Dockerfile" -} - target "api" { inherits = ["default"] context = "services/api" - contexts = { - "lagoon/yarn-workspace-builder": "target:yarn-workspace-builder" - } + dockerfile = "Dockerfile" labels = { "org.opencontainers.image.title": "lagoon-core/api - the API service for Lagoon" } @@ -196,9 +188,6 @@ target "actions-handler" { target "auth-server" { inherits = ["default"] context = "services/auth-server" - contexts = { - "lagoon/yarn-workspace-builder": "target:yarn-workspace-builder" - } labels = { "org.opencontainers.image.title": "lagoon-core/auth-server - the auth-server service for Lagoon" } diff --git a/docker-compose.local-dev.yaml b/docker-compose.local-dev.yaml index 6528773686..d3d063103f 100644 --- a/docker-compose.local-dev.yaml +++ b/docker-compose.local-dev.yaml @@ -6,21 +6,17 @@ volumes: services: api-db-init: volumes: - - ./services/api/database:/app/services/api/database - - ./node-packages:/app/node-packages:delegated - - /app/node-packages/commons/dist + - ./services/api/database:/app/database api-lagoon-migrations: volumes: - - ./services/api/src:/app/services/api/src + - ./services/api/src:/app/src api: - command: ./node_modules/.bin/tsc-watch --build --incremental --onSuccess "node -r dotenv-extended/config dist/index" + command: ./node_modules/.bin/tsc-watch --incremental --onSuccess "node -r dotenv-extended/config dist/index" volumes: - - ./services/api/database:/app/services/api/database - - ./services/api/src:/app/services/api/src - - ./node-packages:/app/node-packages:delegated - - /app/node-packages/commons/dist + - ./services/api/database:/app/database + - ./services/api/src:/app/src auth-server: - command: yarn run dev + command: pnpm run dev volumes: - ./services/auth-server/src:/app/services/auth-server/src keycloak: diff --git a/docker-compose.yaml b/docker-compose.yaml index 8330cf9720..e5a2a92196 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -56,8 +56,8 @@ services: api-db-init: image: ${IMAGE_REPO:-lagoon}/api:${IMAGE_REPO_API_TAG:-${IMAGE_REPO_TAG:-latest}} command: > - sh -c "./node_modules/.bin/knex migrate:list --cwd /app/services/api/database - && ./node_modules/.bin/knex migrate:latest --cwd /app/services/api/database" + sh -c "./node_modules/.bin/knex migrate:list --cwd /app/database + && ./node_modules/.bin/knex migrate:latest --cwd /app/database" depends_on: api-db: condition: service_started diff --git a/docs/contributing-to-lagoon/api-consolidation-refactoring-playbook.md b/docs/contributing-to-lagoon/api-consolidation-refactoring-playbook.md new file mode 100644 index 0000000000..efa796b21b --- /dev/null +++ b/docs/contributing-to-lagoon/api-consolidation-refactoring-playbook.md @@ -0,0 +1,172 @@ +# API Consolidation Refactoring Playbook + +A repeatable set of prompts to consolidate the Lagoon monorepo JavaScript services into a standalone API service. Each prompt builds on the previous; prompts 1–4 can be reordered if the structure differs, but 5→6→7→8 must be sequential. + +--- + +## Prompt 1 — Remove auth-server from the yarn workspace + +``` +Remove the auth-server service from the yarn workspace in the monorepo root package.json. +Convert it to a fully standalone pnpm service with its own package.json, pnpm-lock.yaml, +tsconfig.json, and ESLint v9 flat config (eslint.config.mjs). Fix any TypeScript errors +that arise. Do not change its Dockerfile or functionality. +``` + +--- + +## Prompt 2 — Eliminate the yarn-workspace-builder stage + +``` +The yarn-workspace-builder Docker stage (yarn-workspace-builder/Dockerfile) currently +installs workspace dependencies for the API. Inline its functionality directly into the +services/api/Dockerfile as a multi-stage build, then remove the yarn-workspace-builder +directory. Update docker-bake.hcl and the Makefile accordingly. +``` + +--- + +## Prompt 3 — Consolidate node-packages/commons into services/api + +``` +Move the node-packages/commons package into services/api/commons. Update the root +package.json workspaces array, all tsconfig.json extends/references, all jest configs, +all eslint configs, and all relative path references throughout the monorepo. +Delete the node-packages/ directory entirely. +``` + +--- + +## Prompt 4 — Remove root-level JS/TS infrastructure files + +``` +Delete the root tsconfig.json and root jest.config.js. Confirm nothing extends or imports +them. Merge any relevant jest project configuration into services/api/jest.config.js. +``` + +--- + +## Prompt 5 — Merge commons inline into services/api/src + +``` +Move all source files from services/api/commons/src/ into services/api/src/commons/. +Update services/api/tsconfig.json to add a paths alias mapping @lagoon/commons to +./src/commons. Update services/api/jest.config.js with a moduleNameMapper for the same. +Merge all commons package.json dependencies into services/api/package.json. +Change the build script from "tsc --build" to "tsc". Simplify the Dockerfile to remove +the separate commons build step. Delete services/api/commons/. +Update root package.json workspaces to only ["services/api"]. +``` + +--- + +## Prompt 6 — Replace all @lagoon/commons imports with relative paths + +``` +Rewrite every file under services/api/src/ that imports from @lagoon/commons to use +correct relative paths instead (e.g. @lagoon/commons/util/config → +../../commons/util/config). The correct relative path depends on each file's depth +relative to src/commons/. After rewriting, remove the tsconfig paths/baseUrl entries +and the jest moduleNameMapper for @lagoon/commons — they are no longer needed. +Do not use module-alias or any other runtime alias package. +``` + +--- + +## Prompt 7 — Remove yarn workspaces entirely; make services/api fully standalone + +``` +Remove yarn workspaces from the monorepo entirely and make services/api a fully +standalone Node.js service: + +1. Copy .env.defaults into services/api/.env.defaults and delete the root copy. +2. Move yarn.lock into services/api/yarn.lock. +3. Delete root package.json and root node_modules/. +4. Rewrite services/api/Dockerfile to be self-contained: + - Build context is services/api/ + - WORKDIR /app + - COPY package.json yarn.lock ./ + - Stage 1: yarn install --frozen-lockfile + - Stage 2: copy node_modules, copy source, yarn build +5. Create services/api/.dockerignore (node_modules, dist, .git, *.log). +6. Update docker-bake.hcl: api target context = "services/api", dockerfile = "Dockerfile". +7. Update docker-compose.yaml and docker-compose.local-dev.yaml: all volume mounts and + knex --cwd paths from /app/services/api/... → /app/... +8. Update local-dev/kubectl-patches/api.yaml mountPaths from /app/services/api → /app. +9. Update .vscode/launch.json and services/api/.vscode/launch.json remoteRoot to /app. +10. Update .github/workflows/test-db-migrations.yml knex --cwd paths. +11. Update all affected docs (EN and JA versions). +12. Run yarn install from services/api/ to verify. +``` + +--- + +## Prompt 8 — Add backwards-compatibility symlink for external services + +``` +Some external services still reference /app/services/api/... paths inside the API +container. Add a RUN step to services/api/Dockerfile after yarn build that creates +a symlink: ln -s /app /app/services/api +This makes /app/services/api/{database,src,dist} resolve correctly without changing +the external service. +``` + +--- + +## Prompt 9 — Migrate services/api from yarn to pnpm + +``` +Migrate the services/api service from yarn to pnpm: + +1. If @types/ramda is referenced as a git package (e.g. "github:types/npm-ramda#dist"), + replace it with the latest npm version ("^0.31.x or latest") in package.json. + Also upgrade ramda to the latest version to match. The old git package required + yarn to run its own build scripts, which is incompatible with pnpm. + +2. Run: pnpm install --no-frozen-lockfile + This generates pnpm-lock.yaml. + +3. Create services/api/.npmrc with: + node-linker=hoisted + This makes pnpm use a flat node_modules layout matching yarn behaviour. + +4. Run pnpm install again to regenerate the lockfile with the .npmrc setting. + +5. Delete yarn.lock. + +6. Add to services/api/package.json a "packageManager" field: + "packageManager": "pnpm@" + and a "pnpm" config block allowlisting native build dependencies: + "pnpm": { + "onlyBuiltDependencies": [ + "@newrelic/fn-inspect", "@newrelic/native-metrics", + "@types/ramda", "aws-sdk", "core-js", + "protobufjs", "@apollo/protobufjs" + ] + } + +7. Update all yarn run → pnpm run references in package.json scripts. + +8. Update services/api/Dockerfile: + - COPY package.json pnpm-lock.yaml ./ + - RUN corepack enable && pnpm install --frozen-lockfile + - RUN corepack enable && pnpm build + +9. Update .dockerignore: add yarn.lock to the ignore list. + +10. Update Makefile: replace yarn commands with pnpm equivalents in + local-dev-yarn and k3d/local-dev-patch targets. + +11. Fix any TypeScript errors caused by ramda type version upgrade. + Common patterns after upgrading from old npm-ramda git package: + - R.pathEq(path, val) → R.pathEq(val, path) [API changed in Ramda 0.28] + - R.prop('key', obj) where 'key' is not in type → cast key: 'key' as any + - R.last() returns T | undefined → explicit return type or cast as string + - R.descend(R.prop('key')) → R.descend((r: any) => r.key) + - R.over(R.lensProp('key'), fn) → (obj: any) => ({...obj, key: fn(obj.key)}) + - R.pluck('name')(rows) → R.pluck('name')(rows) as string[] + - R.without(str) where array expected → R.without([str]) + - R.length(nids) where nids is Record → R.length(nids as any[]) + +12. Verify clean build: pnpm build should exit 0 with no errors. +``` diff --git a/docs/contributing-to-lagoon/api-debugging.md b/docs/contributing-to-lagoon/api-debugging.md index d0fbf85b44..2f3a9e8501 100644 --- a/docs/contributing-to-lagoon/api-debugging.md +++ b/docs/contributing-to-lagoon/api-debugging.md @@ -11,10 +11,10 @@ node --inspect=0.0.0.0:9229 ```yaml title="docker-compose.yml" api: image: ${IMAGE_REPO:-lagoon}/api - command: yarn run dev + command: pnpm run dev volumes: - - ./services/api/src:/app/services/api/src - - ./services/api/dist:/app/services/api/dist + - ./services/api/src:/app/src + - ./services/api/dist:/app/dist depends_on: - api-db - local-api-data-watcher-pusher @@ -39,9 +39,9 @@ node --inspect=0.0.0.0:9229 "request": "attach", "port": 9229, "address": "localhost", - "outFiles": ["${workspaceRoot}/app/services/api/dist/**/*.js"], + "outFiles": ["${workspaceRoot}/services/api/dist/**/*.js"], "localRoot": "${workspaceFolder}/services/api", - "remoteRoot": "/app/services/api", + "remoteRoot": "/app", "sourceMaps": true, "protocol": "inspector" } diff --git a/docs/contributing-to-lagoon/developing-lagoon.md b/docs/contributing-to-lagoon/developing-lagoon.md index 08273c1e75..635673b1e2 100644 --- a/docs/contributing-to-lagoon/developing-lagoon.md +++ b/docs/contributing-to-lagoon/developing-lagoon.md @@ -455,13 +455,13 @@ The individual routines relevant to Kubernetes are: ## Local Development -Most services are written in [Node.js](https://nodejs.org/en/docs/). As many of these services share similar Node.js code and Node.js packages, we're using a feature of [Yarn](https://yarnpkg.com/en/docs), called [Yarn workspaces](https://yarnpkg.com/en/docs/workspaces). Yarn workspaces need a `package.json` in the project's root directory that defines the workspaces. +Most services are written in [Node.js](https://nodejs.org/en/docs/). The API service is a standalone Node.js project located in `services/api`, with its own `package.json` and `yarn.lock`. Dependencies are managed directly with Yarn from within that directory. The development of the services can happen directly within Docker. Each container for each service is set up in a way that its source code is mounted into the running container \([see `docker-compose.yml`](../concepts-basics/docker-compose-yml.md)\). Node.js itself is watching the code via `nodemon` , and restarts the Node.js process automatically on a change. ### lagoon-commons -The services not only share many Node.js packages, but also share actual custom code. This code is within `node-packages/lagoon-commons`. It will be automatically symlinked by Yarn workspaces. Additionally, the [`nodemon`](https://www.npmjs.com/package/nodemon) of the services is set up in a way that it checks for changes in `node-packages` and will restart the node process automatically. +The API service contains its own shared utilities and code within `services/api/src/commons`. This code is compiled as part of the API build and changes to it are picked up automatically by `tsc-watch` when running the local development environment. ## Troubleshooting diff --git a/docs/installing-lagoon/gitlab.md b/docs/installing-lagoon/gitlab.md index aa9268c898..d7b910d5a2 100644 --- a/docs/installing-lagoon/gitlab.md +++ b/docs/installing-lagoon/gitlab.md @@ -30,5 +30,5 @@ Not needed for \*most\* installs, but this is configured to integrate Lagoon wit 6. Run the following command in an API pod: ```bash title="Sync with GitLab" - yarn sync:gitlab:all + pnpm run sync:gitlab:all ``` diff --git a/docs/ja/contributing-to-lagoon/api-debugging.md b/docs/ja/contributing-to-lagoon/api-debugging.md index d5d0c16966..e006699fcf 100644 --- a/docs/ja/contributing-to-lagoon/api-debugging.md +++ b/docs/ja/contributing-to-lagoon/api-debugging.md @@ -11,10 +11,10 @@ node --inspect=0.0.0.0:9229 ```yaml title="docker-compose.yml" api: image: ${IMAGE_REPO:-lagoon}/api - command: yarn run dev + command: pnpm run dev volumes: - - ./services/api/src:/app/services/api/src - - ./services/api/dist:/app/services/api/dist + - ./services/api/src:/app/src + - ./services/api/dist:/app/dist depends_on: - api-db - local-api-data-watcher-pusher @@ -39,9 +39,9 @@ node --inspect=0.0.0.0:9229 "request": "attach", "port": 9229, "address": "localhost", - "outFiles": ["${workspaceRoot}/app/services/api/dist/**/*.js"], - "localRoot": "${ "workspaceFolder}/services/api", - "remoteRoot": "/app/services/api", + "outFiles": ["${workspaceRoot}/services/api/dist/**/*.js"], + "localRoot": "${workspaceFolder}/services/api", + "remoteRoot": "/app", "sourceMaps": true, "protocol": "inspector" } diff --git a/docs/ja/contributing-to-lagoon/developing-lagoon.md b/docs/ja/contributing-to-lagoon/developing-lagoon.md index b9154c84a5..f4c6953b51 100644 --- a/docs/ja/contributing-to-lagoon/developing-lagoon.md +++ b/docs/ja/contributing-to-lagoon/developing-lagoon.md @@ -175,13 +175,13 @@ Kubernetesに関連する個々のルーチンは次のとおりです: ## ローカル開発 -ほとんどのサービスは[Node.js](https://nodejs.org/en/docs/)で書かれています。これらのサービスの多くが同様のNode.jsコードとNode.jsパッケージを共有しているため、[Yarn](https://yarnpkg.com/en/docs)という機能を使用しています。これは[Yarn workspaces](https://yarnpkg.com/en/docs/workspaces)と呼ばれます。Yarn workspacesは、ワークスペースを定義するプロジェクトのルートディレクトリに`package.json`が必要です。 +ほとんどのサービスは[Node.js](https://nodejs.org/en/docs/)で書かれています。APIサービスは`services/api`に配置されたスタンドアロンのNode.jsプロジェクトで、独自の`package.json`と`yarn.lock`を持っています。依存関係はそのディレクトリ内から直接Yarnで管理されます。 サービスの開発は、Docker内で直接行うことができます。それぞれのサービスのコンテナは、ソースコードが実行中のコンテナにマウントされるように設定されています([`docker-compose.yml`を参照](../concepts-basics/docker-compose-yml.md))。Node.js自体が`nodemon`を介してコードを監視し、変更があるとNode.jsプロセスを自動的に再起動します。 ### lagoon-commons -サービスは多くのNode.jsパッケージだけでなく、実際のカスタムコードも共有しています。このコードは`node-packages/lagoon-commons`内にあり、自動的にシンボリックリンクされます。 Yarnワークスペースによって管理されています。さらに、サービスの[`nodemon`](https://www.npmjs.com/package/nodemon)は、`node-packages`の変更をチェックし、ノードプロセスを自動的に再起動するように設定されています。 +APIサービスは`services/api/src/commons`内に独自の共有ユーティリティとコードを含んでいます。このコードはAPIビルドの一部としてコンパイルされ、ローカル開発環境を実行中に`tsc-watch`によって自動的に変更が検出されます。 ## トラブルシューティング diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 58fc3c4c29..0000000000 --- a/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - projects: [ - // Projects having their own config - "node-packages/commons", - "services/webhook-handler", - // Everything else (specified in object notation to avoid Jest duplicate config error) - { - testMatch: [ - "/!(cli|node-packages/commons/|services/webhook-handler/){**/__tests__/**/*.js?(x),**/?(*.)(spec|test).js?(x)}" - ], - testPathIgnorePatterns: ["node_modules"] - } - ] -}; diff --git a/k3d-calico.config.yaml.tpl b/k3d-calico.config.yaml.tpl index ff023b150f..926ba9048e 100644 --- a/k3d-calico.config.yaml.tpl +++ b/k3d-calico.config.yaml.tpl @@ -6,7 +6,6 @@ network: k3d volumes: - volume: ${HOME}/.docker/config.json:/var/lib/kubelet/config.json - volume: services:/lagoon/services - - volume: node_packages:/lagoon/node-packages - volume: ${PWD}/calico.yaml:/var/lib/rancher/k3s/server/manifests/calico.yaml registries: config: | diff --git a/k3d.config.yaml.tpl b/k3d.config.yaml.tpl index d821ff620c..bfe1dbe628 100644 --- a/k3d.config.yaml.tpl +++ b/k3d.config.yaml.tpl @@ -6,7 +6,6 @@ network: k3d volumes: - volume: ${HOME}/.docker/config.json:/var/lib/kubelet/config.json - volume: services:/lagoon/services - - volume: node_packages:/lagoon/node-packages registries: create: # pass through for k3d node to share images with the host image: ligfx/k3d-registry-dockerd:v0.5 diff --git a/local-dev/kubectl-patches/api.yaml b/local-dev/kubectl-patches/api.yaml index a288935a06..a01b0d4b0c 100644 --- a/local-dev/kubectl-patches/api.yaml +++ b/local-dev/kubectl-patches/api.yaml @@ -12,14 +12,12 @@ spec: command: - sh - -c - - './node_modules/.bin/tsc-watch --build --incremental --onSuccess "node -r dotenv-extended/config dist/index"' + - './node_modules/.bin/tsc-watch --incremental --onSuccess "node -r dotenv-extended/config dist/index"' volumeMounts: - - mountPath: "/app/services/api/src" + - mountPath: "/app/src" name: api-src - - mountPath: "/app/services/api/dist" + - mountPath: "/app/dist" name: api-dist - - mountPath: "/app/node-packages" - name: node-packages volumes: - name: api-src hostPath: @@ -29,7 +27,3 @@ spec: hostPath: path: "/lagoon/services/api/dist" type: Directory - - name: node-packages - hostPath: - path: "/lagoon/node-packages" - type: Directory \ No newline at end of file diff --git a/local-dev/kubectl-patches/auth-server.yaml b/local-dev/kubectl-patches/auth-server.yaml index 80b4f69ba7..1313f78d1d 100644 --- a/local-dev/kubectl-patches/auth-server.yaml +++ b/local-dev/kubectl-patches/auth-server.yaml @@ -9,15 +9,13 @@ spec: runAsUser: 1000 containers: - name: auth-server - command: ["yarn"] + command: ["pnpm"] args: ["run", "dev"] volumeMounts: - mountPath: "/app/services/auth-server/src" name: auth-server-src - mountPath: "/app/services/auth-server/dist" name: auth-server-dist - - mountPath: "/app/node-packages" - name: node-packages volumes: - name: auth-server-src hostPath: @@ -27,7 +25,3 @@ spec: hostPath: path: "/lagoon/services/auth-server/dist" type: Directory - - name: node-packages - hostPath: - path: "/lagoon/node-packages" - type: Directory \ No newline at end of file diff --git a/local-dev/kubectl-patches/controllerhandler.yaml b/local-dev/kubectl-patches/controllerhandler.yaml index b6d00f6fb5..103940748f 100644 --- a/local-dev/kubectl-patches/controllerhandler.yaml +++ b/local-dev/kubectl-patches/controllerhandler.yaml @@ -16,8 +16,6 @@ spec: name: controllerhandler-src - mountPath: "/app/services/controllerhandler/dist" name: controllerhandler-dist - - mountPath: "/app/node-packages" - name: node-packages volumes: - name: controllerhandler-src hostPath: @@ -26,8 +24,4 @@ spec: - name: controllerhandler-dist hostPath: path: "/lagoon/services/controllerhandler/dist" - type: Directory - - name: node-packages - hostPath: - path: "/lagoon/node-packages" type: Directory \ No newline at end of file diff --git a/local-dev/kubectl-patches/webhook-handler.yaml b/local-dev/kubectl-patches/webhook-handler.yaml index b6d48b9996..8fabac71e2 100644 --- a/local-dev/kubectl-patches/webhook-handler.yaml +++ b/local-dev/kubectl-patches/webhook-handler.yaml @@ -16,8 +16,6 @@ spec: name: webhook-handler-src - mountPath: "/app/services/webhook-handler/dist" name: webhook-handler-dist - - mountPath: "/app/node-packages" - name: node-packages volumes: - name: webhook-handler-src hostPath: @@ -26,8 +24,4 @@ spec: - name: webhook-handler-dist hostPath: path: "/lagoon/services/webhook-handler/dist" - type: Directory - - name: node-packages - hostPath: - path: "/lagoon/node-packages" type: Directory \ No newline at end of file diff --git a/local-dev/kubectl-patches/webhooks2tasks.yaml b/local-dev/kubectl-patches/webhooks2tasks.yaml index ed1e7ba709..28d060cdb4 100644 --- a/local-dev/kubectl-patches/webhooks2tasks.yaml +++ b/local-dev/kubectl-patches/webhooks2tasks.yaml @@ -16,8 +16,6 @@ spec: name: webhooks2tasks-src - mountPath: "/app/services/webhooks2tasks/dist" name: webhooks2tasks-dist - - mountPath: "/app/node-packages" - name: node-packages volumes: - name: webhooks2tasks-src hostPath: @@ -26,8 +24,4 @@ spec: - name: webhooks2tasks-dist hostPath: path: "/lagoon/services/webhooks2tasks/dist" - type: Directory - - name: node-packages - hostPath: - path: "/lagoon/node-packages" type: Directory \ No newline at end of file diff --git a/node-packages/commons/.eslintrc.js b/node-packages/commons/.eslintrc.js deleted file mode 100644 index 96000b1b5e..0000000000 --- a/node-packages/commons/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('eslint-config-lagoon-node'); diff --git a/node-packages/commons/.gitignore b/node-packages/commons/.gitignore deleted file mode 100644 index 5ed153f646..0000000000 --- a/node-packages/commons/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -dist/* -!dist/.gitkeep diff --git a/node-packages/commons/dist/.gitkeep b/node-packages/commons/dist/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/node-packages/commons/jest.config.js b/node-packages/commons/jest.config.js deleted file mode 100644 index 91a2d2c0d3..0000000000 --- a/node-packages/commons/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', -}; \ No newline at end of file diff --git a/node-packages/commons/package.json b/node-packages/commons/package.json deleted file mode 100644 index e496a9270b..0000000000 --- a/node-packages/commons/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@lagoon/commons", - "version": "4.0.0", - "description": "lagoon node commons", - "author": "amazee.io (http://www.amazee.io)", - "license": "MIT", - "scripts": { - "build": "tsc --build", - "format": "prettier-eslint --write '**/*.js'", - "lint": "../eslint-config-lagoon-node/node_modules/.bin/eslint .", - "test": "jest" - }, - "devDependencies": { - "@types/amqp-connection-manager": "^2.0.12", - "@types/jest": "^29.5.12", - "@types/jsonwebtoken": "^9.0.8", - "jest": "^29.7.0", - "prettier": "^3.2.5", - "prettier-eslint-cli": "^8.0.1", - "ts-jest": "^29.1.2", - "tsc-silent": "^1.2.2", - "typescript": "^5.3.3" - }, - "dependencies": { - "amqp-connection-manager": "^4.1.14", - "amqplib": "^0.10.7", - "axios": "^1.12.0", - "jsonwebtoken": "^8.0.1", - "lokka": "^1.7.0", - "node-fetch": "^3.3.2", - "ramda": "0.25.0", - "sha1": "^1.1.1", - "tsc-silent": "^1.2.2", - "winston": "^3" - } -} diff --git a/node-packages/commons/src/notificationCommons.ts b/node-packages/commons/src/notificationCommons.ts deleted file mode 100644 index fd2653ad51..0000000000 --- a/node-packages/commons/src/notificationCommons.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as R from 'ramda'; - -// Note: we're using integer representations of the -// severity in order to be able to use numeric comparisons -// when determining which messages to send - -export const notificationContentTypeToInt = R.cond([ - [R.equals('NONE'), R.always(0)], - [R.equals('UNKNOWN'), R.always(10)], - [R.equals('NEGLIGIBLE'), R.always(20)], - [R.equals('LOW'), R.always(30)], - [R.equals('MEDIUM'), R.always(40)], - [R.equals('HIGH'), R.always(50)], - [R.equals('CRITICAL'), R.always(60)], - ]); - -export const notificationIntToContentType = R.cond([ - [R.equals('0'), R.always('NONE')], - [R.equals('10'), R.always('UNKNOWN')], - [R.equals('20'), R.always('NEGLIGIBLE')], - [R.equals('30'), R.always('LOW')], - [R.equals('40'), R.always('MEDIUM')], - [R.equals('50'), R.always('HIGH')], - [R.equals('60'), R.always('CRITICAL')], - [R.T, R.always('NONE')], - ]); diff --git a/node-packages/commons/tsconfig.json b/node-packages/commons/tsconfig.json deleted file mode 100644 index bb83cc184a..0000000000 --- a/node-packages/commons/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "allowJs": false, - "composite": true, - "declarationMap": true, - "outDir": "./dist", - "rootDir": "./src", - "tsBuildInfoFile": "./dist/.tsbuildinfo" - }, - "include": ["./src"] -} diff --git a/node-packages/eslint-config-lagoon-node/.eslintrc.js b/node-packages/eslint-config-lagoon-node/.eslintrc.js deleted file mode 100644 index edac839e94..0000000000 --- a/node-packages/eslint-config-lagoon-node/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('.'); diff --git a/node-packages/eslint-config-lagoon-node/index.js b/node-packages/eslint-config-lagoon-node/index.js deleted file mode 100644 index 0a4939fca8..0000000000 --- a/node-packages/eslint-config-lagoon-node/index.js +++ /dev/null @@ -1,72 +0,0 @@ -// This is based on a simpler way of sharing ESLint configuration outlined here: -// https://github.com/eslint/eslint/issues/3458#issuecomment-376174514 -module.exports = { - settings: { - 'import/resolver': { - node: { - extensions: ['.js', '.jsx', '.ts', '.tsx'], - }, - 'eslint-import-resolver-typescript': true, - }, - }, - extends: ['airbnb-base'], - plugins: ['typescript'], - env: { es6: true, jest: true, node: true }, - rules: { - // TODO: Flow is gone, do we need this? - // Disable 'arrow-parens': ['error', 'as-needed'] Airbnb stylistic rule for Flow types in comments in single-parameter functions (in the API). This does not cause a regression either, because Prettier will remove other parentheses when it can. - 'arrow-parens': 'off', - // Disable Airbnb stylistic rule because we communicate with services with snake case - camelcase: 'off', - // TODO: Flow is gone, do we need this? - // Rule to enforce function return types. We disable this Airbnb setting because Flow will check our function return types. - 'consistent-return': 'off', - // Fix issue with the way Prettier formats function calls - 'function-paren-newline': 'off', - // Disable Airbnb stylistic rule - 'global-require': 'off', - // Code style rule to enforce import ordering. We disable this Airbnb setting because we use absolute imports for types sometimes after relative imports. - 'import/first': 'off', - // Fix the erroring of dev dependencies in updateSchema script - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: ['**/*.test.js', '**/scripts/*.js'], - }, - ], - // Code style rule to prefer a default export instead of a single named export, currently we disable this Airbnb setting to allow this behavior. We can decide later to turn this on again if we want. - 'import/prefer-default-export': 'off', - // Prettier works better with its default 80 character max-length - 'max-len': 'off', - // Disable Airbnb stylistic rule - 'no-await-in-loop': 'off', - // Conflicts with Prettier's stripping of unnecessary parentheses - 'no-confusing-arrow': 'off', - // Disable Airbnb rule - 'no-console': 'off', - // Rule to restrict usage of confusing code style with mixed boolean operators. We disable this Airbnb setting because Prettier removes "unnecessary parentheses" here and breaks this. - 'no-mixed-operators': 'off', - // Disable Airbnb stylistic rule - 'no-multi-assign': 'off', - // Disable Airbnb stylistic rule - 'no-param-reassign': 'off', - // Disable Airbnb stylistic rule - 'no-plusplus': 'off', - // Disable Airbnb stylistic rule - 'no-restricted-globals': 'off', - // Disable Airbnb stylistic rule - 'no-restricted-syntax': 'off', - // Disable Airbnb stylistic rule - 'no-throw-literal': 'off', - // Rule to prevent prefixing of underscores on variable names. We disable this Airbnb setting because we use some underscore prefixes in our code. - 'no-underscore-dangle': 'off', - // Disable Airbnb stylistic rule - 'no-use-before-define': 'off', - // Disable Airbnb stylistic rule - 'prefer-destructuring': 'off', - // Disable Airbnb stylistic rule - 'prefer-promise-reject-errors': 'off', - // Disable Airbnb stylistic rule - radix: 'off', - }, -}; diff --git a/node-packages/eslint-config-lagoon-node/package.json b/node-packages/eslint-config-lagoon-node/package.json deleted file mode 100644 index 819a022c68..0000000000 --- a/node-packages/eslint-config-lagoon-node/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "eslint-config-lagoon-node", - "version": "1.0.3", - "description": "Lagoon's ESLint configuration for Node", - "main": "index.js", - "repository": "https://github.com/amazeeio/lagoon", - "license": "MIT", - "private": true, - "author": "amazee.io (http://www.amazee.io)", - "contributors": [ - "Karl Horky " - ], - "dependencies": { - "babel-eslint": "^10.1.0", - "eslint": "^8.56.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.7.0" - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 36831e9525..0000000000 --- a/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "lagoon", - "author": "amazee.io (http://www.amazee.io)", - "contributors": [ - "Patrick Stapfer ", - "Karl Horky ", - "Michael Schmid (https://amazee.io)" - ], - "private": true, - "workspaces": [ - "node-packages/*", - "services/*" - ], - "devDependencies": { - "prettier": "^3.2.5", - "yarn-audit-fix": "^10.1.0" - }, - "scripts": { - "glow": "glow check" - }, - "dependencies": {} -} diff --git a/services/api/.dockerignore b/services/api/.dockerignore new file mode 100644 index 0000000000..2e70d95f19 --- /dev/null +++ b/services/api/.dockerignore @@ -0,0 +1,6 @@ +node_modules +dist +.git +*.log +.pnpm-store +**/.pnpm-store diff --git a/services/api/.env.defaults b/services/api/.env.defaults new file mode 100644 index 0000000000..bd7e184506 --- /dev/null +++ b/services/api/.env.defaults @@ -0,0 +1,4 @@ +# These credential information is no secret, it's just for easy +# development. Don't use these values for production deployment! +JWTSECRET=super-secret-string +JWTAUDIENCE=api.dev diff --git a/services/api/.eslintignore b/services/api/.eslintignore new file mode 100644 index 0000000000..c0e647edd1 --- /dev/null +++ b/services/api/.eslintignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +coverage/ +src/lib/keycloak-grant-manager.js diff --git a/services/api/.eslintrc.js b/services/api/.eslintrc.js index 96000b1b5e..525ac304a9 100644 --- a/services/api/.eslintrc.js +++ b/services/api/.eslintrc.js @@ -1 +1,78 @@ -module.exports = require('eslint-config-lagoon-node'); +module.exports = { + parser: '@typescript-eslint/parser', + settings: { + 'import/resolver': { + node: { extensions: ['.js', '.jsx', '.ts', '.tsx'] }, + }, + }, + extends: ['airbnb-base', 'plugin:@typescript-eslint/recommended'], + plugins: ['@typescript-eslint'], + env: { es6: true, jest: true, node: true }, + rules: { + 'arrow-parens': 'off', + camelcase: 'off', + 'consistent-return': 'off', + 'default-case': 'off', + 'default-param-last': 'off', + 'func-names': 'off', + 'function-paren-newline': 'off', + 'global-require': 'off', + 'guard-for-in': 'off', + 'import/extensions': 'off', + 'import/first': 'off', + 'import/no-extraneous-dependencies': ['error', { devDependencies: ['**/*.test.js', '**/*.test.ts', '**/scripts/*.js', '**/mocks.js'] }], + 'import/no-import-module-exports': 'off', + 'import/no-mutable-exports': 'off', + 'import/prefer-default-export': 'off', + 'implicit-arrow-linebreak': 'off', + 'max-len': 'off', + 'no-await-in-loop': 'off', + 'no-case-declarations': 'off', + 'no-confusing-arrow': 'off', + 'no-console': 'off', + 'no-mixed-operators': 'off', + 'no-multi-assign': 'off', + 'no-nested-ternary': 'off', + 'no-param-reassign': 'off', + 'no-plusplus': 'off', + 'no-promise-executor-return': 'off', + 'no-prototype-builtins': 'off', + 'no-restricted-globals': 'off', + 'no-restricted-syntax': 'off', + 'no-return-await': 'off', + 'no-shadow': 'off', + 'no-template-curly-in-string': 'off', + 'no-throw-literal': 'off', + 'no-underscore-dangle': 'off', + 'no-use-before-define': 'off', + 'no-useless-concat': 'off', + 'no-useless-escape': 'off', + 'max-classes-per-file': 'off', + 'prefer-destructuring': 'off', + 'prefer-promise-reject-errors': 'off', + radix: 'off', + // TypeScript ESLint rules — relaxed for pre-existing codebase + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-require-imports': 'off', + '@typescript-eslint/no-this-alias': 'off', + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/no-unused-vars': ['warn', { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }], + 'no-constant-condition': 'off', + '@typescript-eslint/no-var-requires': 'off', + // Disable rules that TypeScript's type checker handles better + 'no-undef': 'off', + // Style rules turned off for pre-existing codebase + 'array-callback-return': 'off', + 'class-methods-use-this': 'off', + 'no-continue': 'off', + 'no-empty': 'off', + 'no-empty-pattern': 'off', + 'no-lonely-if': 'off', + }, +}; diff --git a/services/api/.npmrc b/services/api/.npmrc new file mode 100644 index 0000000000..d67f374883 --- /dev/null +++ b/services/api/.npmrc @@ -0,0 +1 @@ +node-linker=hoisted diff --git a/services/api/.vscode/launch.json b/services/api/.vscode/launch.json index c0cfdce667..87fd668a15 100644 --- a/services/api/.vscode/launch.json +++ b/services/api/.vscode/launch.json @@ -9,7 +9,7 @@ "address": "localhost", "outFiles": ["${workspaceFolder}/dist/**/*.js"], "localRoot": "${workspaceFolder}", - "remoteRoot": "/app/services/api", + "remoteRoot": "/app", "sourceMaps": true, "protocol": "inspector" }, @@ -46,4 +46,4 @@ } } ] -} \ No newline at end of file +} diff --git a/services/api/Dockerfile b/services/api/Dockerfile index 4a731d59c4..5ff6d313a5 100644 --- a/services/api/Dockerfile +++ b/services/api/Dockerfile @@ -1,31 +1,28 @@ ARG LAGOON_GIT_BRANCH -ARG IMAGE_REPO ARG UPSTREAM_REPO ARG UPSTREAM_TAG -# STAGE 1: Loading Image lagoon-node-packages-builder which contains node packages shared by all Node Services -FROM ${IMAGE_REPO:-lagoon}/yarn-workspace-builder AS yarn-workspace-builder -# STAGE 2: specific service Image +# STAGE 1: Install dependencies +FROM ${UPSTREAM_REPO:-uselagoon}/node-22-builder:${UPSTREAM_TAG:-latest} AS builder + +WORKDIR /app +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && pnpm install --frozen-lockfile + +# STAGE 2: API service image FROM ${UPSTREAM_REPO:-uselagoon}/node-22:${UPSTREAM_TAG:-latest} ARG LAGOON_VERSION ENV LAGOON_VERSION=$LAGOON_VERSION -# Copying generated node_modules from the first stage -COPY --from=yarn-workspace-builder /app /app - -# Setting the workdir to the service, all following commands will run from here -WORKDIR /app/services/api/ +WORKDIR /app -# Copying the .env.defaults into the Workdir, as the dotenv system searches within the workdir for it -COPY --from=yarn-workspace-builder /app/.env.defaults . +# Copy installed node_modules from builder stage +COPY --from=builder /app/node_modules ./node_modules -# Copying files from our service +# Copy all service files COPY . . -# Verify that all dependencies have been installed via the yarn-workspace-builder -RUN yarn check --verify-tree - # Making sure we run in production ENV NODE_ENV=production \ LOGSDB_ADMIN_PASSWORD=admin \ @@ -38,6 +35,10 @@ ENV NODE_ENV=production \ # at the same time. So we have a small entrypoint which waits for mariadb to be fully ready. COPY wait-for-mariadb.sh /lagoon/entrypoints/99-wait-for-mariadb.sh -RUN yarn build +RUN corepack enable && pnpm build + +# Backwards-compatibility symlink: some external services still reference +# /app/services/api/... paths. This makes them resolve to /app/... +RUN mkdir -p /app/services && ln -s /app /app/services/api CMD ["node", "-r", "dotenv-extended/config", "dist/index"] diff --git a/services/api/TEST_COVERAGE_PLAN.md b/services/api/TEST_COVERAGE_PLAN.md new file mode 100644 index 0000000000..9ccf9bc3a0 --- /dev/null +++ b/services/api/TEST_COVERAGE_PLAN.md @@ -0,0 +1,349 @@ +# Test Coverage Plan — `services/api` + +## Current State + +8 test suites, 22 tests, 14 snapshots. All passing. + +| File | What it covers | +|------|---------------| +| `src/commons/__tests__/jwt.test.ts` | `createJWTWithoutUserId` — token creation + verification | +| `src/resources/environment/environment.test.js` | `Sql.updateEnvironment`, `Sql.selectEnvironment` | +| `src/resources/notification/notification.test.js` | 4 `Sql.*` query builders | +| `src/resources/openshift/openshift.test.js` | `Sql.*` query builders | +| `src/resources/project/project.test.js` | `Sql.*` query builders | +| `src/resources/sshKey/sshKey.test.js` | `Sql.*` query builders + `validateSshKey` | +| `src/resources/task/advancedtasktoolbox.test.ts` | `canUserSeeTaskDefinition` + permission logic | +| `src/resources/task/models/taskRegistration.test.ts` | `TaskRegistration` model construction | + +Coverage is concentrated in SQL builders and one permission helper. Large swaths of utility code, business-logic helpers, and the auth layer have no tests. + +--- + +## Tier 1 — Pure Utilities (no DB, no network, easy wins) + +These are pure/near-pure functions with no external dependencies. Tests can be written in an afternoon and will never break due to infrastructure changes. + +### 1.1 `src/util/func.ts` + `src/commons/util/func.ts` + +**Why**: Re-exported across the whole codebase. Bugs here are silent and widespread. + +New file: `src/util/__tests__/func.test.ts` + +| Function | Cases to cover | +|----------|---------------| +| `toNumber` | string int, string float, already-a-number, `NaN` input | +| `jsonMerge` | merges on prop, deduplicates, keeps b's version on conflict | +| `arrayDiff` | basic diff, empty arrays, no overlap, full overlap | +| `encodeBase64` / `decodeBase64` | round-trip identity, unicode | +| `encodeJSONBase64` / `decodeJSONBase64` | round-trip identity, nested objects | +| `getErrorMessage` | `Error` instance, plain string, unknown type | +| `isDNS1123Subdomain` | valid subdomain, too long, invalid chars, empty string | +| `isNotNil` / `isNotEmpty` | truthy/falsy cases | + +### 1.2 `src/util/snakeCase.ts` + +**Why**: Used by the knex identifier mapping. A subtle bug here corrupts all column names. + +New file: `src/util/__tests__/snakeCase.test.ts` + +| Case | Example | +|------|---------| +| Lowercase pass-through | `foo` → `foo` | +| Simple camelCase | `fooBar` → `foo_bar` | +| Consecutive uppercase | `fooBAR` → `foo_bar` | +| Already snake | `foo_bar` → `foo_bar` | +| Single char | `A` → `a` | +| `upperCase: true` flag | `fooBar` → `FOO_BAR` | + +### 1.3 `src/util/pickNonNil.ts` + +New file: `src/util/__tests__/pickNonNil.test.ts` + +| Case | Example | +|------|---------| +| Picks requested keys | `{ a: 1, b: 2 }` pick `['a']` → `{ a: 1 }` | +| Omits null values | `{ a: null, b: 2 }` → `{ b: 2 }` | +| Omits undefined values | `{ a: undefined, b: 2 }` → `{ b: 2 }` | +| Keeps falsy non-nil | `{ a: 0, b: false }` → both kept | +| Curried usage | `pickNonNil(['x'])({ x: 1, y: null })` | + +### 1.4 `src/util/convertDateToMYSQLDateTimeFormat.ts` + +New file: `src/util/__tests__/convertDate.test.ts` + +| Case | +|------| +| ISO string → `YYYY-MM-DD HH:mm:ss` | +| `convertDateToMYSQLDateFormat` → `YYYY-MM-DD` only | +| Invalid date string handling | + +### 1.5 `src/resources/sshKey/helpers.ts` — `validateSshKey` + +Already exercised via `sshKey.test.js`. Consider moving to a proper `.test.ts` with typed imports and adding: +- DSS key type +- Key with no trailing comment +- Whitespace-padded input + +### 1.6 `src/resources/task/models/advancedTaskDefinitionArgument.ts` + +New file: `src/resources/task/models/advancedTaskDefinitionArgument.test.ts` + +| Class / Function | Cases | +|-----------------|-------| +| `StringArgument.validateInput` | any string returns `true` | +| `NumberArgument.validateInput` | `'42'` → true, `'3.14'` → true, `'abc'` → false, `''` → false | +| `ArgumentBase.validateInput` | always true | +| `advancedTaskDefinitionTypeFactory` | returns correct class for each type name, throws on unknown | +| `EnvironmentSourceArgument.validateInput` | uses mocked `query()` returning env names | + +--- + +## Tier 2 — SQL Query Builders (snapshot tests) + +These follow the identical pattern to the existing `*.test.js` files: call a `Sql.*` method, snapshot the resulting SQL string. No DB needed. No mocks needed. Ordered by size/value. + +Each should live at `src/resources//.test.ts`. + +### 2.1 `src/resources/task/sql.ts` (335 lines) — highest priority + +Key builders to snapshot: +- `selectTask`, `selectTaskByNameAndEnvironment` +- `insertTask` (all fields, partial fields) +- `updateTask` (patch semantics — only provided fields updated) +- `selectTasksByEnvironmentId` with/without status filter +- `deleteTask` +- Advanced task definition builders: `insertAdvancedTaskDefinition`, `selectAdvancedTaskDefinition`, `updateAdvancedTaskDefinition` + +### 2.2 `src/resources/deployment/sql.ts` (212 lines) + +Key builders: +- `selectDeployment`, `selectDeploymentByNameAndEnvironment` +- `insertDeployment` (all fields) +- `updateDeployment` (patch) +- `selectDeploymentsByEnvironmentId` — includes ordering, status filters, pagination args +- `deleteDeployment` + +### 2.3 `src/resources/routes/sql.ts` (194 lines) + +Key builders: +- `selectRoutesByEnvironmentId` +- `insertRoute`, `updateRoute`, `deleteRoute` +- Autogenerated route builders +- Route annotation builders + +### 2.4 `src/resources/problem/sql.ts` (144 lines) + +Key builders: +- `selectAllProblems` — the parameterised source/severity/envType filtering +- `insertProblem`, `deleteProblem` +- `selectProblemsForEnvironment` + +### 2.5 `src/resources/organization/sql.ts` (130 lines) + +Key builders: +- `selectOrganization`, `insertOrganization`, `updateOrganization`, `deleteOrganization` +- `selectOrganizationProjects`, `addProjectToOrganization` + +### 2.6 `src/resources/backup/sql.ts` (129 lines) + +Key builders: +- `selectBackup`, `insertBackup` +- `selectRestore`, `insertRestore`, `updateRestore` +- `deleteBackupsForEnvironment` + +### 2.7 `src/resources/retentionpolicy/sql.ts` (112 lines) + +Key builders: +- `selectRetentionPolicy`, `insertRetentionPolicy`, `updateRetentionPolicy` + +### 2.8 `src/resources/env-variables/sql.ts` (94 lines) + +Key builders: +- `selectEnvVariablesByProjectId`, `selectEnvVariablesByEnvironmentId` +- `insertEnvVariable` (project scope, environment scope) +- `deleteEnvVariable` + +### 2.9 `src/resources/fact/sql.ts` (91 lines) + +Key builders: +- `insertFact`, `deleteFact`, `selectFacts` +- `selectFactsByEnvironmentId` with name filter + +### 2.10 `src/resources/group/sql.ts` (66 lines) + +Key builders: +- `selectGroup`, `insertGroup`, `updateGroup`, `deleteGroup` + +### 2.11 `src/resources/user/sql.ts` (62 lines) + +Key builders: +- `selectUser`, `insertUser`, `updateUser` + +### 2.12 `src/resources/deploytargetconfig/sql.ts` (61 lines) + +Key builders: +- `selectDeployTargetConfig`, `insertDeployTargetConfig`, `updateDeployTargetConfig` + +### 2.13 `src/resources/audit/sql.ts` (31 lines) + +- `insertAuditLog`, `selectAuditLogs` + +--- + +## Tier 3 — Business Logic with Mocked Dependencies + +These functions contain real branching logic but require mocked DB or auth. Use `jest.fn()` for `sqlClientPool` and `hasPermission`. + +### 3.1 `src/loggers/userActivityLogger.ts` — `parseAndCleanMeta` (regression tests) + +**Why**: This function had a null-reference bug (fixed) and silently strips sensitive data from logs. A regression suite here prevents recurrence. + +New file: `src/loggers/__tests__/userActivityLogger.test.ts` + +| Case | Description | +|------|-------------| +| Keycloak user with `access_token` | Strips token content, keeps safe fields only | +| Legacy user (no `access_token`) | Keeps `username`, `email`, `role`, drops nothing unexpected | +| Null `headers` | Does not throw — returns meta without headers (`!= null` guard) | +| Null `user` | Returns meta with null user | +| Entirely `null` meta | Returns `undefined` / empty cleanly | +| `headers` with `user-agent` | Truncates long user-agent strings | +| Complex nested `payload` | Preserved as-is | + +### 3.2 `src/util/auth.ts` — `extractWsToken` + +**Why**: We added this function to fix WebSocket auth. It has a non-trivial set of fallback paths. + +New file: `src/util/__tests__/auth.test.ts` + +| Case | +|------| +| `connectionParams.authToken` present | +| `connectionParams.Authorization` with `Bearer ` prefix | +| `connectionParams.authorization` (lowercase) | +| `connectionParams.headers.Authorization` | +| `connectionParams.headers.authorization` | +| All null/undefined → returns `null` | +| Strips `Bearer ` prefix correctly | + +### 3.3 `src/resources/task/filters.ts` — `filterAdminTasks` + +New file: `src/resources/task/__tests__/filters.test.ts` + +| Case | +|------| +| No admin tasks in list → all rows returned, permission never checked | +| Admin task present + user has `viewAll` → all rows returned | +| Admin task present + user denied `viewAll` → admin rows removed | +| Mix of admin and non-admin rows → only non-admin survive when denied | +| Empty row list → returns empty array | + +### 3.4 `src/resources/problem/helpers.ts` — `groupByProblemIdentifier` + +New file: `src/resources/problem/__tests__/helpers.test.ts` + +| Case | +|------| +| Single problem → object with one key | +| Multiple problems, same identifier → grouped under one key | +| Multiple problems, different identifiers → separate keys | +| Empty array → empty object | + +### 3.5 `src/resources/routes/helpers.ts` — constants + `isDNS1123Subdomain` usage + +New file: `src/resources/routes/__tests__/helpers.test.ts` + +| Case | +|------| +| `AnnotationLimit`, `PathRoutesLimit`, `AlternativeDomainsLimit` constants have expected values | +| `validateRouteAnnotation` (if present) — valid/invalid annotation formats | + +### 3.6 `src/resources/environment/validators.ts` + +New file: `src/resources/environment/__tests__/validators.test.ts` + +Use `jest.fn()` + `moduleNameMapper` for `sqlClientPool`. + +| Validator | Cases | +|-----------|-------| +| `environmentExists` | row found → no throw; row missing → throws Unauthorized | +| `environmentsHaveSameProject` | same project id → ok; different project ids → throws | +| `environmentHasService` | service present → ok; service missing → throws | + +--- + +## Tier 4 — Integration / Model Layer + +Higher effort but covers the most critical path: Keycloak + DB interactions. +These are the highest-value tests but require the most setup time. Recommended for a second pass. + +### 4.1 `src/models/user.ts` (1,017 lines) + +Mock both `sqlClientPool` (via `moduleNameMapper`) and `keycloak-admin` client. Focus on: +- `getUserById`, `getUserByUsername` — basic DB reads +- `addUser`, `updateUser` — DB writes + Keycloak calls +- `getUserRoles` — role aggregation logic + +### 4.2 `src/models/group.ts` (1,137 lines) + +Same approach. Focus on: +- `getGroupMembership` — the pagination loop +- `addUserToGroup`, `removeUserFromGroup` +- `getGroupsByProjectId` + +### 4.3 `src/authMiddleware.ts` + +Use `supertest` or plain `req`/`res` mocks: +- Valid JWT token → `req.user` set +- Expired token → 401 +- Missing token → 401 +- Bearer format vs `lagoon-token` format + +--- + +## Not Worth Unit Testing + +These are better covered by integration/E2E tests (the existing k3d test suite): + +- All `resolvers.ts` files — too tightly coupled to the full GraphQL context object; mock cost exceeds benefit +- `src/server.ts` — server bootstrap, WebSocket/SSE setup +- `src/routes/*.ts` — thin HTTP handlers +- `src/clients/*.ts` — thin third-party client wrappers +- `src/mocks.js` / `src/typeDefs.js` — GraphQL schema definitions + +--- + +## Execution Order + +``` +Phase 1 (Tier 1 + Tier 3 selected items) — ~1–2 days + ✦ src/util/__tests__/func.test.ts + ✦ src/util/__tests__/snakeCase.test.ts + ✦ src/util/__tests__/pickNonNil.test.ts + ✦ src/util/__tests__/convertDate.test.ts + ✦ src/resources/task/models/advancedTaskDefinitionArgument.test.ts + ✦ src/resources/task/__tests__/filters.test.ts + ✦ src/loggers/__tests__/userActivityLogger.test.ts (regression) + ✦ src/util/__tests__/auth.test.ts (extractWsToken regression) + +Phase 2 (Tier 2 SQL builders) — ~1 day + ✦ Start with task, deployment, routes (largest/most complex) + ✦ Then backup, org, problem, retentionpolicy + ✦ Then env-variables, fact, group, user, deploytargetconfig, audit + +Phase 3 (Tier 3 remaining) — ~1 day + ✦ validators, problem helpers, routes constants + +Phase 4 (Tier 4 model layer) — ~2–3 days + ✦ user model, group model, authMiddleware +``` + +## Coverage Target + +| Category | Current | After Phase 1+2 | After Phase 3+4 | +|----------|---------|-----------------|-----------------| +| Utility functions | ~0% | ~90% | ~90% | +| SQL builders | ~20% | ~85% | ~85% | +| Business logic helpers | ~5% | ~30% | ~70% | +| Auth / models | ~5% | ~10% | ~50% | +| Resolvers | 0% | 0% | 0% (E2E) | diff --git a/services/api/__mocks__/mariadb.js b/services/api/__mocks__/mariadb.js new file mode 100644 index 0000000000..316ca7e5e8 --- /dev/null +++ b/services/api/__mocks__/mariadb.js @@ -0,0 +1,19 @@ +// Manual mock for mariadb (ESM package — not compatible with Jest's CJS transform). +// Tests that transitively import src/clients/sqlClient will use this instead of +// the real driver. Unit tests should never hit a real database. + +const mockPool = { + query: jest.fn().mockResolvedValue([]), + getConnection: jest.fn().mockResolvedValue({ + query: jest.fn().mockResolvedValue([]), + release: jest.fn(), + beginTransaction: jest.fn().mockResolvedValue(undefined), + commit: jest.fn().mockResolvedValue(undefined), + rollback: jest.fn().mockResolvedValue(undefined), + }), + end: jest.fn().mockResolvedValue(undefined), +}; + +const createPool = jest.fn(() => mockPool); + +module.exports = { createPool, default: { createPool } }; diff --git a/services/api/__mocks__/node-fetch.js b/services/api/__mocks__/node-fetch.js new file mode 100644 index 0000000000..48f1755e38 --- /dev/null +++ b/services/api/__mocks__/node-fetch.js @@ -0,0 +1,17 @@ +// Manual mock for node-fetch 3.x (ESM-only — incompatible with Jest CJS mode). +// Returns a minimal fetch implementation sufficient for unit tests. + +const mockResponse = (body = '{}', status = 200) => ({ + ok: status >= 200 && status < 300, + status, + statusText: 'OK', + headers: { get: jest.fn(() => null) }, + json: jest.fn().mockResolvedValue(JSON.parse(body)), + text: jest.fn().mockResolvedValue(body), + arrayBuffer: jest.fn().mockResolvedValue(Buffer.from(body)), +}); + +const fetch = jest.fn().mockResolvedValue(mockResponse()); + +module.exports = fetch; +module.exports.default = fetch; diff --git a/services/api/__mocks__/redis.js b/services/api/__mocks__/redis.js new file mode 100644 index 0000000000..7ce85e4deb --- /dev/null +++ b/services/api/__mocks__/redis.js @@ -0,0 +1,21 @@ +// Manual mock for redis v3 — prevents a live TCP connection from being opened +// at import time (which keeps Jest's open-handle detector busy). + +const EventEmitter = require('events'); + +const createMockClient = () => { + const client = new EventEmitter(); + client.get = jest.fn((key, cb) => cb && cb(null, null)); + client.set = jest.fn((key, value, cb) => cb && cb(null, 'OK')); + client.del = jest.fn((key, cb) => cb && cb(null, 1)); + client.expire = jest.fn((key, ttl, cb) => cb && cb(null, 1)); + client.quit = jest.fn((cb) => { if (cb) cb(null, 'OK'); }); + client.end = jest.fn(); + client.connected = false; + client.ready = false; + return client; +}; + +module.exports = { + createClient: jest.fn(createMockClient), +}; diff --git a/services/api/database/knexfile.js b/services/api/database/knexfile.js index 2211bb058b..43b53ef61e 100644 --- a/services/api/database/knexfile.js +++ b/services/api/database/knexfile.js @@ -3,22 +3,22 @@ /** * @type { Object. } */ - module.exports = { +module.exports = { - client: 'mysql2', - connection: { - host : process.env.API_DB_HOST || 'api-db', - port : process.env.API_DB_PORT || 3306, - user : process.env.API_DB_USERNAME || 'api', - password : process.env.API_DB_PASSWORD || 'api', - database : process.env.API_DB_DATABASE || 'infrastructure' - }, - pool: { - min: 2, - max: 10 - }, - migrations: { - tableName: 'knex_migrations' - }, + client: 'mysql2', + connection: { + host: process.env.API_DB_HOST || 'api-db', + port: process.env.API_DB_PORT || 3306, + user: process.env.API_DB_USERNAME || 'api', + password: process.env.API_DB_PASSWORD || 'api', + database: process.env.API_DB_DATABASE || 'infrastructure', + }, + pool: { + min: 2, + max: 10, + }, + migrations: { + tableName: 'knex_migrations', + }, }; diff --git a/services/api/database/migrations/20220908065119_initial_db.js b/services/api/database/migrations/20220908065119_initial_db.js index 412cc63e90..44a36bfc70 100644 --- a/services/api/database/migrations/20220908065119_initial_db.js +++ b/services/api/database/migrations/20220908065119_initial_db.js @@ -2,315 +2,313 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ - exports.up = async function(knex) { - DBExists = await knex.schema.hasTable('deployment'); - if (!DBExists) { - return knex.schema - .createTable('advanced_task_definition', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 300).notNullable(); - table.specificType('description', 'text').notNullable(); - table.string('image', 2000).defaultTo(''); - table.string('service', 100); - table.string('type', 100).notNullable(); - table.integer('environment'); - table.integer('project'); - table.string('group_name', 300); - table.enu('permission', ['GUEST', 'DEVELOPER', 'MAINTAINER']).defaultTo('GUEST'); - table.specificType('command', 'text'); - table.string('confirmation_text', 2000); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.timestamp('deleted').notNullable(); - table.unique(['name', 'environment', 'project', 'group_name'], {indexName: 'name', storageEngineIndexType: 'hash'}); - }) - .createTable('advanced_task_definition_argument', function (table) { - table.increments('id').notNullable().primary(); - table.integer('advanced_task_definition'); - table.string('name', 300).notNullable(); - table.string('display_name', 500); - table.enu('type', ['NUMERIC', 'STRING', 'ENVIRONMENT_SOURCE_NAME', 'ENVIRONMENT_SOURCE_NAME_EXCLUDE_SELF']); - table.unique(['advanced_task_definition', 'name'], {indexName: 'advanced_task_definition_argument_unique'}); - }) - .createTable('backup_restore', function (table) { - table.increments('id').notNullable().primary(); - table.string('backup_id', 300).unique({indexName:'backup_id'}); - table.enu('status', ['pending', 'successful', 'failed']).defaultTo('pending'); - table.string('restore_location', 300); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - }) - .createTable('deploy_target_config', function (table) { - table.increments('id').notNullable().primary(); - table.integer('project'); - table.integer('weight').notNullable().defaultTo(0); - table.string('branches', 300); - table.string('pullrequests', 300); - table.integer('deploy_target'); - table.string('deploy_target_project_pattern', 300); - }) - .createTable('deployment', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 100).notNullable(); - table.enu('status', ['new', 'pending', 'running', 'cancelled', 'error', 'failed', 'complete']).notNullable(); - table.datetime('created').notNullable().defaultTo(knex.fn.now()); - table.datetime('started'); - table.datetime('completed'); - table.integer('environment').notNullable(); - table.string('remote_id', 50); - table.integer('priority'); - table.string('bulk_id', 50); - table.string('bulk_name', 100); - table.index('environment', 'deployment_environment'); - }) - .createTable('env_vars', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 300).notNullable(); - table.text('value').notNullable(); - table.enu('scope',['global', 'build', 'runtime', 'container_registry', 'internal_container_registry']).notNullable().defaultTo('global'); - table.integer('project'); - table.integer('environment'); - table.unique(['name', 'project'], {indexName: 'name_project'}); - table.unique(['name', 'environment'], {indexName: 'name_environment'}); - }) - .createTable('environment', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 100); - table.integer('project'); - table.enu('deploy_type', ['branch', 'pullrequest', 'promote']).notNullable(); - table.string('deploy_base_ref', 250); - table.string('deploy_head_ref', 250); - table.string('deploy_title', 300); - table.enu('environment_type', ['production', 'development']).notNullable(); - table.integer('auto_idle', 1).notNullable().defaultTo(1); - table.string('openshift_project_name', 100); - table.string('route', 300); - table.text('routes'); - table.text('monitoring_urls'); - table.integer('openshift'); - table.string('openshift_project_pattern', 300); - table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.timestamp('deleted').notNullable(); - table.unique(['project', 'name', 'deleted'], {indexName: 'project_name_deleted'}); - }) - .createTable('environment_backup', function (table) { - table.increments('id').notNullable().primary(); - table.integer('environment'); - table.string('source', 300); - table.string('backup_id', 300).unique({indexName:'backup_id'}); - table.timestamp('created').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')); - table.timestamp('deleted').notNullable(); - table.index('environment', 'backup_environment'); - }) - .createTable('environment_fact', function (table) { - table.increments('id').notNullable().primary(); - table.integer('environment').defaultTo(null); - table.string('service', 300); - table.string('name', 300).notNullable(); - table.string('value', 300).notNullable(); - table.enu('type', ['TEXT', 'URL', 'SEMVER']).defaultTo('TEXT'); - table.string('source', 300).defaultTo(''); - table.specificType('description', 'text'); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.specificType('category', 'text'); - table.boolean('key_fact').notNullable().defaultTo(0); - table.unique(['environment', 'name', 'source'], {indexName: 'environment_fact'}); - }) - .createTable('environment_fact_reference', function (table) { - table.increments('id').notNullable().primary(); - table.integer('fid').notNullable(); - table.string('name', 300).notNullable(); - table.unique(['fid', 'name'], {indexName: 'fid'}); - }) - .createTable('environment_problem', function (table) { - table.increments('id').notNullable().primary(); - table.integer('environment'); - table.string('severity', 300).defaultTo(''); - table.decimal('severity_score', 1, 1).defaultTo(0); - table.string('identifier', 100).notNullable(); - table.string('lagoon_service', 100).defaultTo(''); - table.string('source', 300).defaultTo(''); - table.string('associated_package', 300).defaultTo(''); - table.specificType('description', 'text'); - table.string('version', 100).defaultTo(''); - table.string('fixed_version', 300).defaultTo(''); - table.string('links', 300).defaultTo(''); - table.json('data'); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.timestamp('deleted').notNullable(); - table.unique(['environment', 'lagoon_service', 'version', 'identifier', 'deleted'], {indexName: 'environment'}); - }) - .createTable('environment_service', function (table) { - table.increments('id').notNullable().primary(); - table.integer('environment').notNullable(); - table.string('name', 100).notNullable(); - }) - .createTable('environment_storage', function (table) { - table.increments('id').notNullable().primary(); - table.integer('environment'); - table.string('persistent_storage_claim', 100); - table.bigInteger('bytes_used'); - table.date('updated'); - table.unique(['environment', 'persistent_storage_claim', 'updated'], {indexName: 'environment_persistent_storage_claim_updated'}); - }) - .createTable('notification_email', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).unique({indexName: 'name'}); - table.string('email_address', 300); - }) - .createTable('notification_microsoftteams', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).unique({indexName: 'name'}); - table.string('webhook', 512); - }) - .createTable('notification_rocketchat', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).unique({indexName: 'name'}); - table.string('webhook', 300); - table.string('channel', 300); - }) - .createTable('notification_slack', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).unique({indexName: 'name'}); - table.string('webhook', 300); - table.string('channel', 300); - }) - .createTable('notification_webhook', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).unique({indexName: 'name'}); - table.string('webhook', 2000); - }) - .createTable('openshift', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).unique({indexName: 'name'}); - table.string('console_url', 300); - table.string('token', 2000); - table.string('router_pattern', 300); - table.string('ssh_host', 300); - table.string('ssh_port', 50); - table.string('monitoring_config', 2048); - table.string('friendly_name', 100); - table.string('cloud_provider', 100); - table.string('cloud_region', 100); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.string('build_image', 2000); - }) - .createTable('problem_harbor_scan_matcher', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 100).notNullable(); - table.text('description'); - table.string('default_lagoon_project', 300); - table.string('default_lagoon_environment', 300); - table.string('default_lagoon_service_name', 300); - table.string('regex', 300).notNullable(); - }) - .createTable('project', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 100).unique({indexName: 'name'}); - table.string('git_url', 300); - table.string('availability', 50).defaultTo('STANDARD').notNullable(); - table.string('subfolder', 300); - table.string('routerpattern', 300); - table.string('active_systems_deploy', 300); - table.string('active_systems_promote', 300); - table.string('active_systems_remove', 300); - table.string('active_systems_task', 300); - table.string('active_systems_misc', 300); - table.string('branches', 300); - table.string('pullrequests', 300); - table.string('production_environment', 100); - table.text('production_routes'); - table.string('production_alias', 100).notNullable().defaultTo('lagoon-production') - table.string('standby_production_environment', 100); - table.text('standby_routes'); - table.string('standby_alias', 100).notNullable().defaultTo('lagoon-standby') - table.integer('auto_idle', 1).notNullable().defaultTo(1); - table.integer('storage_calc', 1).notNullable().defaultTo(1); - table.integer('problems_ui', 1).notNullable().defaultTo(0); - table.integer('facts_ui', 1).notNullable().defaultTo(0); - table.integer('deployments_disabled', 1).notNullable().defaultTo(0); - table.integer('production_build_priority').notNullable().defaultTo(6); - table.integer('development_build_priority').notNullable().defaultTo(5); - table.integer('openshift'); - table.string('openshift_project_pattern', 300); - table.integer('development_environments_limit'); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.string('private_key', 5000); - table.json('metadata'); - table.string('router_pattern', 300).defaultTo(null); - }) - .createTable('project_notification', function (table) { - table.integer('nid'); - table.integer('pid'); - table.enu('type', ['slack','rocketchat','microsoftteams','email', 'webhook']).notNullable(); - table.enu('content_type', ['deployment', 'problem']).notNullable(); - table.integer('notification_severity_threshold').notNullable().defaultTo(0); - table.primary(['nid', 'pid', 'type']); - }) - .createTable('s3_file', function (table) { - table.increments('id').notNullable().primary(); - table.string('filename', 100).notNullable(); - table.text('s3_key').notNullable(); - table.datetime('created').notNullable().defaultTo(knex.fn.now()); - table.datetime('deleted').notNullable(); - }) - .createTable('ssh_key', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 100).notNullable(); - table.string('key_value', 5000).notNullable(); - table.enu('key_type', ['ssh-rsa', 'ssh-ed25519','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521']).notNullable().defaultTo('ssh-rsa'); - table.specificType('key_fingerprint', 'CHAR(51)').unique({indexName: 'key_fingerprint'}); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - }) - .createTable('task', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 100).notNullable(); - table.string('task_name', 100); - table.integer('environment').notNullable(); - table.string('service', 100).notNullable(); - table.text('command').notNullable(); - table.enu('status', ['new', 'pending', 'running', 'cancelled', 'error', 'failed', 'complete', 'active', 'succeeded']).notNullable(); - table.datetime('created').notNullable().defaultTo(knex.fn.now()); - table.datetime('started'); - table.datetime('completed'); - table.string('remote_id', 50); - table.enu('type', ['standard', 'advanced']).defaultTo('standard'); - table.string('advanced_image', 2000); - table.text('advanced_payload'); - table.index('environment', 'task_environment'); - }) - .createTable('task_file', function (table) { - table.integer('tid'); - table.integer('fid'); - table.primary(['tid', 'fid']); - }) - .createTable('user_ssh_key', function (table) { - table.specificType('usid', 'CHAR(36)'); - table.integer('skid'); - table.primary(['usid', 'skid']); - }) - .createTable('workflow', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).notNullable(); - table.string('event', 300).notNullable(); - table.integer('project', 11).notNullable(); - table.integer('advanced_task_definition', 11).notNullable(); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.timestamp('deleted').notNullable(); - }) - } - else { - return knex.schema - } +exports.up = async function (knex) { + DBExists = await knex.schema.hasTable('deployment'); + if (!DBExists) { + return knex.schema + .createTable('advanced_task_definition', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 300).notNullable(); + table.specificType('description', 'text').notNullable(); + table.string('image', 2000).defaultTo(''); + table.string('service', 100); + table.string('type', 100).notNullable(); + table.integer('environment'); + table.integer('project'); + table.string('group_name', 300); + table.enu('permission', ['GUEST', 'DEVELOPER', 'MAINTAINER']).defaultTo('GUEST'); + table.specificType('command', 'text'); + table.string('confirmation_text', 2000); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.timestamp('deleted').notNullable(); + table.unique(['name', 'environment', 'project', 'group_name'], { indexName: 'name', storageEngineIndexType: 'hash' }); + }) + .createTable('advanced_task_definition_argument', (table) => { + table.increments('id').notNullable().primary(); + table.integer('advanced_task_definition'); + table.string('name', 300).notNullable(); + table.string('display_name', 500); + table.enu('type', ['NUMERIC', 'STRING', 'ENVIRONMENT_SOURCE_NAME', 'ENVIRONMENT_SOURCE_NAME_EXCLUDE_SELF']); + table.unique(['advanced_task_definition', 'name'], { indexName: 'advanced_task_definition_argument_unique' }); + }) + .createTable('backup_restore', (table) => { + table.increments('id').notNullable().primary(); + table.string('backup_id', 300).unique({ indexName: 'backup_id' }); + table.enu('status', ['pending', 'successful', 'failed']).defaultTo('pending'); + table.string('restore_location', 300); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + }) + .createTable('deploy_target_config', (table) => { + table.increments('id').notNullable().primary(); + table.integer('project'); + table.integer('weight').notNullable().defaultTo(0); + table.string('branches', 300); + table.string('pullrequests', 300); + table.integer('deploy_target'); + table.string('deploy_target_project_pattern', 300); + }) + .createTable('deployment', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 100).notNullable(); + table.enu('status', ['new', 'pending', 'running', 'cancelled', 'error', 'failed', 'complete']).notNullable(); + table.datetime('created').notNullable().defaultTo(knex.fn.now()); + table.datetime('started'); + table.datetime('completed'); + table.integer('environment').notNullable(); + table.string('remote_id', 50); + table.integer('priority'); + table.string('bulk_id', 50); + table.string('bulk_name', 100); + table.index('environment', 'deployment_environment'); + }) + .createTable('env_vars', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 300).notNullable(); + table.text('value').notNullable(); + table.enu('scope', ['global', 'build', 'runtime', 'container_registry', 'internal_container_registry']).notNullable().defaultTo('global'); + table.integer('project'); + table.integer('environment'); + table.unique(['name', 'project'], { indexName: 'name_project' }); + table.unique(['name', 'environment'], { indexName: 'name_environment' }); + }) + .createTable('environment', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 100); + table.integer('project'); + table.enu('deploy_type', ['branch', 'pullrequest', 'promote']).notNullable(); + table.string('deploy_base_ref', 250); + table.string('deploy_head_ref', 250); + table.string('deploy_title', 300); + table.enu('environment_type', ['production', 'development']).notNullable(); + table.integer('auto_idle', 1).notNullable().defaultTo(1); + table.string('openshift_project_name', 100); + table.string('route', 300); + table.text('routes'); + table.text('monitoring_urls'); + table.integer('openshift'); + table.string('openshift_project_pattern', 300); + table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.timestamp('deleted').notNullable(); + table.unique(['project', 'name', 'deleted'], { indexName: 'project_name_deleted' }); + }) + .createTable('environment_backup', (table) => { + table.increments('id').notNullable().primary(); + table.integer('environment'); + table.string('source', 300); + table.string('backup_id', 300).unique({ indexName: 'backup_id' }); + table.timestamp('created').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')); + table.timestamp('deleted').notNullable(); + table.index('environment', 'backup_environment'); + }) + .createTable('environment_fact', (table) => { + table.increments('id').notNullable().primary(); + table.integer('environment').defaultTo(null); + table.string('service', 300); + table.string('name', 300).notNullable(); + table.string('value', 300).notNullable(); + table.enu('type', ['TEXT', 'URL', 'SEMVER']).defaultTo('TEXT'); + table.string('source', 300).defaultTo(''); + table.specificType('description', 'text'); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.specificType('category', 'text'); + table.boolean('key_fact').notNullable().defaultTo(0); + table.unique(['environment', 'name', 'source'], { indexName: 'environment_fact' }); + }) + .createTable('environment_fact_reference', (table) => { + table.increments('id').notNullable().primary(); + table.integer('fid').notNullable(); + table.string('name', 300).notNullable(); + table.unique(['fid', 'name'], { indexName: 'fid' }); + }) + .createTable('environment_problem', (table) => { + table.increments('id').notNullable().primary(); + table.integer('environment'); + table.string('severity', 300).defaultTo(''); + table.decimal('severity_score', 1, 1).defaultTo(0); + table.string('identifier', 100).notNullable(); + table.string('lagoon_service', 100).defaultTo(''); + table.string('source', 300).defaultTo(''); + table.string('associated_package', 300).defaultTo(''); + table.specificType('description', 'text'); + table.string('version', 100).defaultTo(''); + table.string('fixed_version', 300).defaultTo(''); + table.string('links', 300).defaultTo(''); + table.json('data'); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.timestamp('deleted').notNullable(); + table.unique(['environment', 'lagoon_service', 'version', 'identifier', 'deleted'], { indexName: 'environment' }); + }) + .createTable('environment_service', (table) => { + table.increments('id').notNullable().primary(); + table.integer('environment').notNullable(); + table.string('name', 100).notNullable(); + }) + .createTable('environment_storage', (table) => { + table.increments('id').notNullable().primary(); + table.integer('environment'); + table.string('persistent_storage_claim', 100); + table.bigInteger('bytes_used'); + table.date('updated'); + table.unique(['environment', 'persistent_storage_claim', 'updated'], { indexName: 'environment_persistent_storage_claim_updated' }); + }) + .createTable('notification_email', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).unique({ indexName: 'name' }); + table.string('email_address', 300); + }) + .createTable('notification_microsoftteams', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).unique({ indexName: 'name' }); + table.string('webhook', 512); + }) + .createTable('notification_rocketchat', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).unique({ indexName: 'name' }); + table.string('webhook', 300); + table.string('channel', 300); + }) + .createTable('notification_slack', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).unique({ indexName: 'name' }); + table.string('webhook', 300); + table.string('channel', 300); + }) + .createTable('notification_webhook', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).unique({ indexName: 'name' }); + table.string('webhook', 2000); + }) + .createTable('openshift', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).unique({ indexName: 'name' }); + table.string('console_url', 300); + table.string('token', 2000); + table.string('router_pattern', 300); + table.string('ssh_host', 300); + table.string('ssh_port', 50); + table.string('monitoring_config', 2048); + table.string('friendly_name', 100); + table.string('cloud_provider', 100); + table.string('cloud_region', 100); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.string('build_image', 2000); + }) + .createTable('problem_harbor_scan_matcher', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 100).notNullable(); + table.text('description'); + table.string('default_lagoon_project', 300); + table.string('default_lagoon_environment', 300); + table.string('default_lagoon_service_name', 300); + table.string('regex', 300).notNullable(); + }) + .createTable('project', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 100).unique({ indexName: 'name' }); + table.string('git_url', 300); + table.string('availability', 50).defaultTo('STANDARD').notNullable(); + table.string('subfolder', 300); + table.string('routerpattern', 300); + table.string('active_systems_deploy', 300); + table.string('active_systems_promote', 300); + table.string('active_systems_remove', 300); + table.string('active_systems_task', 300); + table.string('active_systems_misc', 300); + table.string('branches', 300); + table.string('pullrequests', 300); + table.string('production_environment', 100); + table.text('production_routes'); + table.string('production_alias', 100).notNullable().defaultTo('lagoon-production'); + table.string('standby_production_environment', 100); + table.text('standby_routes'); + table.string('standby_alias', 100).notNullable().defaultTo('lagoon-standby'); + table.integer('auto_idle', 1).notNullable().defaultTo(1); + table.integer('storage_calc', 1).notNullable().defaultTo(1); + table.integer('problems_ui', 1).notNullable().defaultTo(0); + table.integer('facts_ui', 1).notNullable().defaultTo(0); + table.integer('deployments_disabled', 1).notNullable().defaultTo(0); + table.integer('production_build_priority').notNullable().defaultTo(6); + table.integer('development_build_priority').notNullable().defaultTo(5); + table.integer('openshift'); + table.string('openshift_project_pattern', 300); + table.integer('development_environments_limit'); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.string('private_key', 5000); + table.json('metadata'); + table.string('router_pattern', 300).defaultTo(null); + }) + .createTable('project_notification', (table) => { + table.integer('nid'); + table.integer('pid'); + table.enu('type', ['slack', 'rocketchat', 'microsoftteams', 'email', 'webhook']).notNullable(); + table.enu('content_type', ['deployment', 'problem']).notNullable(); + table.integer('notification_severity_threshold').notNullable().defaultTo(0); + table.primary(['nid', 'pid', 'type']); + }) + .createTable('s3_file', (table) => { + table.increments('id').notNullable().primary(); + table.string('filename', 100).notNullable(); + table.text('s3_key').notNullable(); + table.datetime('created').notNullable().defaultTo(knex.fn.now()); + table.datetime('deleted').notNullable(); + }) + .createTable('ssh_key', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 100).notNullable(); + table.string('key_value', 5000).notNullable(); + table.enu('key_type', ['ssh-rsa', 'ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521']).notNullable().defaultTo('ssh-rsa'); + table.specificType('key_fingerprint', 'CHAR(51)').unique({ indexName: 'key_fingerprint' }); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + }) + .createTable('task', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 100).notNullable(); + table.string('task_name', 100); + table.integer('environment').notNullable(); + table.string('service', 100).notNullable(); + table.text('command').notNullable(); + table.enu('status', ['new', 'pending', 'running', 'cancelled', 'error', 'failed', 'complete', 'active', 'succeeded']).notNullable(); + table.datetime('created').notNullable().defaultTo(knex.fn.now()); + table.datetime('started'); + table.datetime('completed'); + table.string('remote_id', 50); + table.enu('type', ['standard', 'advanced']).defaultTo('standard'); + table.string('advanced_image', 2000); + table.text('advanced_payload'); + table.index('environment', 'task_environment'); + }) + .createTable('task_file', (table) => { + table.integer('tid'); + table.integer('fid'); + table.primary(['tid', 'fid']); + }) + .createTable('user_ssh_key', (table) => { + table.specificType('usid', 'CHAR(36)'); + table.integer('skid'); + table.primary(['usid', 'skid']); + }) + .createTable('workflow', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).notNullable(); + table.string('event', 300).notNullable(); + table.integer('project', 11).notNullable(); + table.integer('advanced_task_definition', 11).notNullable(); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.timestamp('deleted').notNullable(); + }); + } + return knex.schema; }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - DBExists = await knex.schema.hasTable('deployment'); - if (DBExists) { - return knex.schema - } +exports.down = async function (knex) { + DBExists = await knex.schema.hasTable('deployment'); + if (DBExists) { + return knex.schema; + } }; diff --git a/services/api/database/migrations/20220908065152_v2_10_validation.js b/services/api/database/migrations/20220908065152_v2_10_validation.js index d00e01f8e2..c991b588a1 100644 --- a/services/api/database/migrations/20220908065152_v2_10_validation.js +++ b/services/api/database/migrations/20220908065152_v2_10_validation.js @@ -2,20 +2,19 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ - exports.up = async function(knex) { - lagoonVersion = await knex.schema.hasColumn('openshift', 'build_image'); - if (!lagoonVersion) { - throw new Error("Exiting - build older than 2.10, please update to version 2.10 first") - } - else { - return knex.schema - } +exports.up = async function (knex) { + lagoonVersion = await knex.schema.hasColumn('openshift', 'build_image'); + if (!lagoonVersion) { + throw new Error('Exiting - build older than 2.10, please update to version 2.10 first'); + } else { + return knex.schema; + } }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { - return knex.schema +exports.down = function (knex) { + return knex.schema; }; diff --git a/services/api/database/migrations/20221129221152_queuedstatus.js b/services/api/database/migrations/20221129221152_queuedstatus.js index 6c3fc989c7..d85e00644f 100644 --- a/services/api/database/migrations/20221129221152_queuedstatus.js +++ b/services/api/database/migrations/20221129221152_queuedstatus.js @@ -2,31 +2,31 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ - exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('deployment', (table) => { - table.string('build_step'); + table.string('build_step'); }) .raw(`ALTER TABLE task MODIFY status ENUM("new", "pending", "running", "cancelled", "error", "failed", "complete", "active", "succeeded", "queued") NOT NULL;`) .raw(`ALTER TABLE deployment - MODIFY status ENUM("new", "pending", "running", "cancelled", "error", "failed", "complete", "queued") NOT NULL;`) + MODIFY status ENUM("new", "pending", "running", "cancelled", "error", "failed", "complete", "queued") NOT NULL;`); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - // cant alter enums in place, so drop the column first :D - return knex.schema +exports.down = async function (knex) { + // cant alter enums in place, so drop the column first :D + return knex.schema .alterTable('deployment', (table) => { - table.dropColumn('build_step'); + table.dropColumn('build_step'); }) - .raw(`UPDATE task SET status="pending" WHERE status="queued";`) - .raw(`UPDATE deployment SET status="pending" WHERE status="queued";`) + .raw('UPDATE task SET status="pending" WHERE status="queued";') + .raw('UPDATE deployment SET status="pending" WHERE status="queued";') .raw(`ALTER TABLE task MODIFY status ENUM("new", "pending", "running", "cancelled", "error", "failed", "complete", "active", "succeeded") NOT NULL;`) .raw(`ALTER TABLE deployment - MODIFY status ENUM("new", "pending", "running", "cancelled", "error", "failed", "complete") NOT NULL;`) + MODIFY status ENUM("new", "pending", "running", "cancelled", "error", "failed", "complete") NOT NULL;`); }; diff --git a/services/api/database/migrations/20230119064648_add_admin_advanced_tasks.js b/services/api/database/migrations/20230119064648_add_admin_advanced_tasks.js index b46b580ce9..a101dda86b 100644 --- a/services/api/database/migrations/20230119064648_add_admin_advanced_tasks.js +++ b/services/api/database/migrations/20230119064648_add_admin_advanced_tasks.js @@ -2,22 +2,22 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('advanced_task_definition', (table) => { - table.boolean('show_ui').notNullable().defaultTo(1); - table.boolean('admin_task').notNullable().defaultTo(0); - }) + table.boolean('show_ui').notNullable().defaultTo(1); + table.boolean('admin_task').notNullable().defaultTo(0); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('advanced_task_definition', (table) => { - table.dropColumn('show_ui'); - table.dropColumn('admin_task'); - }) + table.dropColumn('show_ui'); + table.dropColumn('admin_task'); + }); }; diff --git a/services/api/database/migrations/20230202013434_remove_monitoring_urls.js b/services/api/database/migrations/20230202013434_remove_monitoring_urls.js index 881ddc3664..349b05110a 100644 --- a/services/api/database/migrations/20230202013434_remove_monitoring_urls.js +++ b/services/api/database/migrations/20230202013434_remove_monitoring_urls.js @@ -2,20 +2,20 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex.schema - .alterTable('environment', (table) => { - table.dropColumn('monitoring_urls'); - }); + .alterTable('environment', (table) => { + table.dropColumn('monitoring_urls'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { +exports.down = function (knex) { return knex.schema - .alterTable('environment', (table) => { - table.text('monitoring_urls'); - }); + .alterTable('environment', (table) => { + table.text('monitoring_urls'); + }); }; diff --git a/services/api/database/migrations/20230207210000_task_admin_fields.js b/services/api/database/migrations/20230207210000_task_admin_fields.js index f90adddd5b..16224784c1 100644 --- a/services/api/database/migrations/20230207210000_task_admin_fields.js +++ b/services/api/database/migrations/20230207210000_task_admin_fields.js @@ -2,46 +2,46 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('task', (table) => { - table.boolean('admin_only_view').notNullable().defaultTo(0); - table.boolean('deploy_token_injection').notNullable().defaultTo(0); - table.boolean('project_key_injection').notNullable().defaultTo(0); + table.boolean('admin_only_view').notNullable().defaultTo(0); + table.boolean('deploy_token_injection').notNullable().defaultTo(0); + table.boolean('project_key_injection').notNullable().defaultTo(0); }) .alterTable('advanced_task_definition', (table) => { - table.boolean('admin_only_view').notNullable().defaultTo(0); - table.boolean('deploy_token_injection').notNullable().defaultTo(0); - table.boolean('project_key_injection').notNullable().defaultTo(0); + table.boolean('admin_only_view').notNullable().defaultTo(0); + table.boolean('deploy_token_injection').notNullable().defaultTo(0); + table.boolean('project_key_injection').notNullable().defaultTo(0); }) - .raw(`UPDATE advanced_task_definition atd SET atd.admin_only_view = atd.show_ui XOR 1;`) // invert show_ui to admin_only_view - .raw(`UPDATE advanced_task_definition atd SET atd.deploy_token_injection = atd.admin_task, atd.project_key_injection = atd.admin_task;`) // turn admin_task into deploy_token and project_key + .raw('UPDATE advanced_task_definition atd SET atd.admin_only_view = atd.show_ui XOR 1;') // invert show_ui to admin_only_view + .raw('UPDATE advanced_task_definition atd SET atd.deploy_token_injection = atd.admin_task, atd.project_key_injection = atd.admin_task;') // turn admin_task into deploy_token and project_key .alterTable('advanced_task_definition', (table) => { - table.dropColumn('show_ui'); - table.dropColumn('admin_task'); - }) + table.dropColumn('show_ui'); + table.dropColumn('admin_task'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('task', (table) => { - table.dropColumn('admin_only_view'); - table.dropColumn('deploy_token_injection'); - table.dropColumn('project_key_injection'); + table.dropColumn('admin_only_view'); + table.dropColumn('deploy_token_injection'); + table.dropColumn('project_key_injection'); }) .alterTable('advanced_task_definition', (table) => { - table.boolean('show_ui').notNullable().defaultTo(1); - table.boolean('admin_task').notNullable().defaultTo(0); + table.boolean('show_ui').notNullable().defaultTo(1); + table.boolean('admin_task').notNullable().defaultTo(0); }) - .raw(`UPDATE advanced_task_definition atd SET atd.show_ui = atd.admin_only_view XOR 1;`) // invert admin_only_view to show_ui - .raw(`UPDATE advanced_task_definition atd SET atd.admin_task = atd.deploy_token_injection;`) // revert deploy_token to admin_task + .raw('UPDATE advanced_task_definition atd SET atd.show_ui = atd.admin_only_view XOR 1;') // invert admin_only_view to show_ui + .raw('UPDATE advanced_task_definition atd SET atd.admin_task = atd.deploy_token_injection;') // revert deploy_token to admin_task .alterTable('advanced_task_definition', (table) => { - table.dropColumn('admin_only_view'); - table.dropColumn('deploy_token_injection'); - table.dropColumn('project_key_injection'); - }) + table.dropColumn('admin_only_view'); + table.dropColumn('deploy_token_injection'); + table.dropColumn('project_key_injection'); + }); }; diff --git a/services/api/database/migrations/20230208080000_disabled_deploytargets.js b/services/api/database/migrations/20230208080000_disabled_deploytargets.js index f9fa64768b..42bdfe0535 100644 --- a/services/api/database/migrations/20230208080000_disabled_deploytargets.js +++ b/services/api/database/migrations/20230208080000_disabled_deploytargets.js @@ -2,20 +2,20 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('openshift', (table) => { - table.boolean('disabled').notNullable().defaultTo(0); - }) + table.boolean('disabled').notNullable().defaultTo(0); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('openshift', (table) => { - table.dropColumn('disabled'); - }) + table.dropColumn('disabled'); + }); }; diff --git a/services/api/database/migrations/20230227080000_envvar_index.js b/services/api/database/migrations/20230227080000_envvar_index.js index d2be22cdf2..cc3518f8cd 100644 --- a/services/api/database/migrations/20230227080000_envvar_index.js +++ b/services/api/database/migrations/20230227080000_envvar_index.js @@ -2,22 +2,22 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('env_vars', (table) => { - table.index('environment', 'environment_index'); - table.index('project', 'project_index'); - }) + table.index('environment', 'environment_index'); + table.index('project', 'project_index'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('env_vars', (table) => { - table.dropIndex('environment', 'environment_index'); - table.dropIndex('project', 'project_index'); - }) + table.dropIndex('environment', 'environment_index'); + table.dropIndex('project', 'project_index'); + }); }; diff --git a/services/api/database/migrations/20230329000000_add_project_build_image.js b/services/api/database/migrations/20230329000000_add_project_build_image.js index 78350a8ef7..6ecafbd5c2 100644 --- a/services/api/database/migrations/20230329000000_add_project_build_image.js +++ b/services/api/database/migrations/20230329000000_add_project_build_image.js @@ -2,20 +2,20 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('project', (table) => { - table.string('build_image', 2000); - }) + table.string('build_image', 2000); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('project', (table) => { - table.dropColumn('build_image'); - }) + table.dropColumn('build_image'); + }); }; diff --git a/services/api/database/migrations/20230703000000_advanced_task_default_optional.js b/services/api/database/migrations/20230703000000_advanced_task_default_optional.js index 4e5d011332..0a58bd78d6 100644 --- a/services/api/database/migrations/20230703000000_advanced_task_default_optional.js +++ b/services/api/database/migrations/20230703000000_advanced_task_default_optional.js @@ -2,22 +2,22 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('advanced_task_definition_argument', (table) => { - table.text('default_value'); - table.boolean('optional').notNullable().defaultTo(0); - }) + table.text('default_value'); + table.boolean('optional').notNullable().defaultTo(0); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('advanced_task_definition_argument', (table) => { - table.dropColumn('default_value'); - table.dropColumn('optional'); - }) + table.dropColumn('default_value'); + table.dropColumn('optional'); + }); }; diff --git a/services/api/database/migrations/20230706000000_shared_baas_bucket.js b/services/api/database/migrations/20230706000000_shared_baas_bucket.js index b527720cff..c879b41feb 100644 --- a/services/api/database/migrations/20230706000000_shared_baas_bucket.js +++ b/services/api/database/migrations/20230706000000_shared_baas_bucket.js @@ -2,27 +2,27 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('openshift', (table) => { - table.string('shared_baas_bucket_name', 300); + table.string('shared_baas_bucket_name', 300); }) .alterTable('project', (table) => { - table.boolean('shared_baas_bucket').notNullable().defaultTo(1); // defaults all new projects to use the shared baas bucket + table.boolean('shared_baas_bucket').notNullable().defaultTo(1); // defaults all new projects to use the shared baas bucket }) - .raw(`UPDATE project SET shared_baas_bucket=0;`) // update existing projects already deployed to retain their project specific buckets + .raw('UPDATE project SET shared_baas_bucket=0;'); // update existing projects already deployed to retain their project specific buckets }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('project', (table) => { - table.dropColumn('shared_baas_bucket'); + table.dropColumn('shared_baas_bucket'); }) .alterTable('openshift', (table) => { - table.dropColumn('shared_baas_bucket_name'); - }) + table.dropColumn('shared_baas_bucket_name'); + }); }; diff --git a/services/api/database/migrations/20230801000000_organizations.js b/services/api/database/migrations/20230801000000_organizations.js index be8a466ad6..72ea986c78 100644 --- a/services/api/database/migrations/20230801000000_organizations.js +++ b/services/api/database/migrations/20230801000000_organizations.js @@ -2,54 +2,53 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - organizations = await knex.schema.hasTable('organization'); - if (!organizations) { - return knex.schema - .createTable('organization', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 300).unique({indexName: 'name'}); - table.string('friendly_name', 300).notNullable(); - table.specificType('description', 'text').notNullable(); - table.integer('quota_project').defaultTo(1).notNullable(); - table.integer('quota_group').defaultTo(10).notNullable(); - table.integer('quota_notification').defaultTo(10).notNullable(); - table.integer('quota_environment').defaultTo(5).notNullable(); - table.integer('quota_route').defaultTo(5).notNullable(); - }) - .createTable('organization_deploy_target', function (table) { - table.integer('orgid'); - table.integer('dtid'); - table.primary(['orgid', 'dtid']); - }) - .alterTable('notification_email', function (table) { - table.integer('organization'); - }) - .alterTable('notification_microsoftteams', function (table) { - table.integer('organization'); - }) - .alterTable('notification_rocketchat', function (table) { - table.integer('organization'); - }) - .alterTable('notification_slack', function (table) { - table.integer('organization'); - }) - .alterTable('notification_webhook', function (table) { - table.integer('organization'); - }) - .alterTable('project', function (table) { - table.integer('organization'); - }) - } - else { - return knex.schema - } +exports.up = async function (knex) { + organizations = await knex.schema.hasTable('organization'); + if (!organizations) { + return knex.schema + .createTable('organization', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 300).unique({ indexName: 'name' }); + table.string('friendly_name', 300).notNullable(); + table.specificType('description', 'text').notNullable(); + table.integer('quota_project').defaultTo(1).notNullable(); + table.integer('quota_group').defaultTo(10).notNullable(); + table.integer('quota_notification').defaultTo(10).notNullable(); + table.integer('quota_environment').defaultTo(5).notNullable(); + table.integer('quota_route').defaultTo(5).notNullable(); + }) + .createTable('organization_deploy_target', (table) => { + table.integer('orgid'); + table.integer('dtid'); + table.primary(['orgid', 'dtid']); + }) + .alterTable('notification_email', (table) => { + table.integer('organization'); + }) + .alterTable('notification_microsoftteams', (table) => { + table.integer('organization'); + }) + .alterTable('notification_rocketchat', (table) => { + table.integer('organization'); + }) + .alterTable('notification_slack', (table) => { + table.integer('organization'); + }) + .alterTable('notification_webhook', (table) => { + table.integer('organization'); + }) + .alterTable('project', (table) => { + table.integer('organization'); + }); + } + + return knex.schema; }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema; }; diff --git a/services/api/database/migrations/20231222000000_delete_orphaned_insights.js b/services/api/database/migrations/20231222000000_delete_orphaned_insights.js index 1f3a279b73..8b217c740a 100644 --- a/services/api/database/migrations/20231222000000_delete_orphaned_insights.js +++ b/services/api/database/migrations/20231222000000_delete_orphaned_insights.js @@ -2,27 +2,27 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { - return Promise.all([ - knex.schema.raw(` +exports.up = function (knex) { + return Promise.all([ + knex.schema.raw(` DELETE ef FROM environment_fact ef LEFT JOIN environment e ON ef.environment = e.id WHERE e.id IS NULL OR e.deleted != '0000-00-00 00:00:00' OR e.project not in (select id from project) `), - knex.schema.raw(` + knex.schema.raw(` DELETE ep FROM environment_problem ep LEFT JOIN environment e ON ep.environment = e.id WHERE e.id IS NULL OR e.deleted != '0000-00-00 00:00:00' OR e.project not in (select id from project) `), - ]); - }; + ]); +}; - /** +/** * @param { import("knex").Knex } knex * @returns { Promise } */ - exports.down = function(knex) { - return knex.schema; - }; +exports.down = function (knex) { + return knex.schema; +}; diff --git a/services/api/database/migrations/20240108000000_fix_env_orphans.js b/services/api/database/migrations/20240108000000_fix_env_orphans.js index 7bb0072a31..a14a4b21fe 100644 --- a/services/api/database/migrations/20240108000000_fix_env_orphans.js +++ b/services/api/database/migrations/20240108000000_fix_env_orphans.js @@ -2,11 +2,11 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex('environment') +exports.up = async function (knex) { + return knex('environment') .leftJoin('project', 'environment.project', 'project.id') .whereNull('project.id') - .andWhere('environment.deleted','0000-00-00 00:00:00') + .andWhere('environment.deleted', '0000-00-00 00:00:00') .update('deleted', knex.raw('NOW()')); }; @@ -14,6 +14,6 @@ exports.up = async function(knex) { * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema; }; diff --git a/services/api/database/migrations/20240113000000_add_advanced_task_systemwide.js b/services/api/database/migrations/20240113000000_add_advanced_task_systemwide.js index 5e1fbd1ae6..8cc1c60cbc 100644 --- a/services/api/database/migrations/20240113000000_add_advanced_task_systemwide.js +++ b/services/api/database/migrations/20240113000000_add_advanced_task_systemwide.js @@ -2,20 +2,20 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('advanced_task_definition', (table) => { - table.boolean('system_wide').notNullable().defaultTo(0); - }) + table.boolean('system_wide').notNullable().defaultTo(0); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('advanced_task_definition', (table) => { - table.dropColumn('system_wide'); - }) + table.dropColumn('system_wide'); + }); }; diff --git a/services/api/database/migrations/20240114000000_environment_services.js b/services/api/database/migrations/20240114000000_environment_services.js index 9882c89c9d..430528059e 100644 --- a/services/api/database/migrations/20240114000000_environment_services.js +++ b/services/api/database/migrations/20240114000000_environment_services.js @@ -2,33 +2,33 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema - .raw(`DELETE o FROM environment_service AS o LEFT JOIN (SELECT MIN(id) AS id FROM environment_service GROUP BY environment, name) AS i ON o.id = i.id WHERE i.id IS NULL;`) - .alterTable('environment_service', function (table) { - table.string('type', 300); - table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.unique(['name', 'environment'], {indexName: 'service_environment'}); - }) - .createTable('environment_service_container', function (table) { - table.integer('service_id'); - table.string('name', 300); - table.unique(['service_id', 'name'], {indexName: 'service_container'}); +exports.up = async function (knex) { + return knex.schema + .raw('DELETE o FROM environment_service AS o LEFT JOIN (SELECT MIN(id) AS id FROM environment_service GROUP BY environment, name) AS i ON o.id = i.id WHERE i.id IS NULL;') + .alterTable('environment_service', (table) => { + table.string('type', 300); + table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.unique(['name', 'environment'], { indexName: 'service_environment' }); }) + .createTable('environment_service_container', (table) => { + table.integer('service_id'); + table.string('name', 300); + table.unique(['service_id', 'name'], { indexName: 'service_container' }); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('environment_service', (table) => { - table.dropColumn('type'); - table.dropColumn('updated'); - table.dropColumn('created'); - table.dropUnique(['name', 'environment'], 'service_environment'); + table.dropColumn('type'); + table.dropColumn('updated'); + table.dropColumn('created'); + table.dropUnique(['name', 'environment'], 'service_environment'); }) - .dropTable('environment_service_container') -}; \ No newline at end of file + .dropTable('environment_service_container'); +}; diff --git a/services/api/database/migrations/20240115000000_taskdeployment_source.js b/services/api/database/migrations/20240115000000_taskdeployment_source.js index 3015a6f8e8..d5994671de 100644 --- a/services/api/database/migrations/20240115000000_taskdeployment_source.js +++ b/services/api/database/migrations/20240115000000_taskdeployment_source.js @@ -2,30 +2,30 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema - .alterTable('task', function (table) { - table.enu('source_type', ['api']); - table.string('source_user', 300); - }) - .alterTable('deployment', function (table) { - table.enu('source_type', ['api', 'webhook']); - table.string('source_user', 300); +exports.up = async function (knex) { + return knex.schema + .alterTable('task', (table) => { + table.enu('source_type', ['api']); + table.string('source_user', 300); }) + .alterTable('deployment', (table) => { + table.enu('source_type', ['api', 'webhook']); + table.string('source_user', 300); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('task', (table) => { - table.dropColumn('source_type'); - table.dropColumn('source_user'); + table.dropColumn('source_type'); + table.dropColumn('source_user'); }) .alterTable('deployment', (table) => { - table.dropColumn('source_type'); - table.dropColumn('source_user'); - }) -}; \ No newline at end of file + table.dropColumn('source_type'); + table.dropColumn('source_user'); + }); +}; diff --git a/services/api/database/migrations/20240223000000_bytesused_kibused.js b/services/api/database/migrations/20240223000000_bytesused_kibused.js index dec6c35c63..4327f63e3b 100644 --- a/services/api/database/migrations/20240223000000_bytesused_kibused.js +++ b/services/api/database/migrations/20240223000000_bytesused_kibused.js @@ -2,20 +2,20 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema - .alterTable('environment_storage', function (table) { - table.renameColumn('bytes_used', 'kib_used'); - }) +exports.up = async function (knex) { + return knex.schema + .alterTable('environment_storage', (table) => { + table.renameColumn('bytes_used', 'kib_used'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema - .alterTable('environment_storage', function (table) { - table.renameColumn('kib_used', 'bytes_used'); - }) -}; \ No newline at end of file +exports.down = async function (knex) { + return knex.schema + .alterTable('environment_storage', (table) => { + table.renameColumn('kib_used', 'bytes_used'); + }); +}; diff --git a/services/api/database/migrations/20240312000000_group_projects.js b/services/api/database/migrations/20240312000000_group_projects.js index 3c7e3602da..3bcbe9db64 100644 --- a/services/api/database/migrations/20240312000000_group_projects.js +++ b/services/api/database/migrations/20240312000000_group_projects.js @@ -2,39 +2,38 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - group_projects = await knex.schema.hasTable('kc_group_projects'); - if (!group_projects) { - return knex.schema - // this table holds the main group to organization id association - .createTable('kc_group_organization', function (table) { - table.increments('id').notNullable().primary(); - table.string('group_id', 50).notNullable(); - table.integer('organization_id').notNullable(); - table.unique(['group_id', 'organization_id'], {indexName: 'group_organization'}); - }) - // this table holds the main group to organization id association - .createTable('kc_group_projects', function (table) { - table.increments('id').notNullable().primary(); - table.string('group_id', 50).notNullable(); - table.integer('project_id').notNullable(); - table.unique(['group_id', 'project_id'], {indexName: 'group_project'}); - }) - } - else { - return knex.schema - } +exports.up = async function (knex) { + group_projects = await knex.schema.hasTable('kc_group_projects'); + if (!group_projects) { + return knex.schema + // this table holds the main group to organization id association + .createTable('kc_group_organization', (table) => { + table.increments('id').notNullable().primary(); + table.string('group_id', 50).notNullable(); + table.integer('organization_id').notNullable(); + table.unique(['group_id', 'organization_id'], { indexName: 'group_organization' }); + }) + // this table holds the main group to organization id association + .createTable('kc_group_projects', (table) => { + table.increments('id').notNullable().primary(); + table.string('group_id', 50).notNullable(); + table.integer('project_id').notNullable(); + table.unique(['group_id', 'project_id'], { indexName: 'group_project' }); + }); + } + + return knex.schema; }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - // caveats around this are that the rollback can only work while data is still saved in keycloak attributes - // once we remove that duplication of attribute into keycloak, this rollback would result in data loss for group>project associations - // for any group project associations made after the attribute removal - return knex.schema +exports.down = async function (knex) { + // caveats around this are that the rollback can only work while data is still saved in keycloak attributes + // once we remove that duplication of attribute into keycloak, this rollback would result in data loss for group>project associations + // for any group project associations made after the attribute removal + return knex.schema .dropTable('kc_group_organization') - .dropTable('kc_group_projects') + .dropTable('kc_group_projects'); }; diff --git a/services/api/database/migrations/20240312010000_remove_activesystems.js b/services/api/database/migrations/20240312010000_remove_activesystems.js index cc6ff5f63a..a90f488ee0 100644 --- a/services/api/database/migrations/20240312010000_remove_activesystems.js +++ b/services/api/database/migrations/20240312010000_remove_activesystems.js @@ -2,28 +2,28 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex.schema - .alterTable('project', (table) => { - table.dropColumn('active_systems_deploy'); - table.dropColumn('active_systems_promote'); - table.dropColumn('active_systems_remove'); - table.dropColumn('active_systems_task'); - table.dropColumn('active_systems_misc'); - }); + .alterTable('project', (table) => { + table.dropColumn('active_systems_deploy'); + table.dropColumn('active_systems_promote'); + table.dropColumn('active_systems_remove'); + table.dropColumn('active_systems_task'); + table.dropColumn('active_systems_misc'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { +exports.down = function (knex) { return knex.schema - .alterTable('project', (table) => { - table.string('active_systems_deploy', 300); - table.string('active_systems_promote', 300); - table.string('active_systems_remove', 300); - table.string('active_systems_task', 300); - table.string('active_systems_misc', 300); - }); + .alterTable('project', (table) => { + table.string('active_systems_deploy', 300); + table.string('active_systems_promote', 300); + table.string('active_systems_remove', 300); + table.string('active_systems_task', 300); + table.string('active_systems_misc', 300); + }); }; diff --git a/services/api/database/migrations/20240313000000_delete_problem_harbor_scan_matcher.js b/services/api/database/migrations/20240313000000_delete_problem_harbor_scan_matcher.js index fb5582b36a..00c04ba0bb 100644 --- a/services/api/database/migrations/20240313000000_delete_problem_harbor_scan_matcher.js +++ b/services/api/database/migrations/20240313000000_delete_problem_harbor_scan_matcher.js @@ -2,22 +2,22 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { - return knex.schema.dropTable('problem_harbor_scan_matcher'); +exports.up = function (knex) { + return knex.schema.dropTable('problem_harbor_scan_matcher'); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { - return knex.schema.createTable('problem_harbor_scan_matcher', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 100).notNullable(); - table.text('description'); - table.string('default_lagoon_project', 300); - table.string('default_lagoon_environment', 300); - table.string('default_lagoon_service_name', 300); - table.string('regex', 300).notNullable(); - }); +exports.down = function (knex) { + return knex.schema.createTable('problem_harbor_scan_matcher', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 100).notNullable(); + table.text('description'); + table.string('default_lagoon_project', 300); + table.string('default_lagoon_environment', 300); + table.string('default_lagoon_service_name', 300); + table.string('regex', 300).notNullable(); + }); }; diff --git a/services/api/database/migrations/20240501000000_organization_quotas.js b/services/api/database/migrations/20240501000000_organization_quotas.js index 773fa91636..6b32cabc39 100644 --- a/services/api/database/migrations/20240501000000_organization_quotas.js +++ b/services/api/database/migrations/20240501000000_organization_quotas.js @@ -2,30 +2,30 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex.schema - .raw(`ALTER TABLE organization ALTER quota_route SET DEFAULT -1;`) - .raw(`ALTER TABLE organization ALTER quota_project SET DEFAULT -1;`) - .raw(`ALTER TABLE organization ALTER quota_group SET DEFAULT -1;`) - .raw(`ALTER TABLE organization ALTER quota_notification SET DEFAULT -1;`) - .raw(`ALTER TABLE organization ALTER quota_environment SET DEFAULT -1;`) - .alterTable('organization', function (table) { - table.timestamp('created').defaultTo(knex.fn.now()); - }) + .raw('ALTER TABLE organization ALTER quota_route SET DEFAULT -1;') + .raw('ALTER TABLE organization ALTER quota_project SET DEFAULT -1;') + .raw('ALTER TABLE organization ALTER quota_group SET DEFAULT -1;') + .raw('ALTER TABLE organization ALTER quota_notification SET DEFAULT -1;') + .raw('ALTER TABLE organization ALTER quota_environment SET DEFAULT -1;') + .alterTable('organization', (table) => { + table.timestamp('created').defaultTo(knex.fn.now()); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { +exports.down = function (knex) { return knex.schema - .raw(`ALTER TABLE organization ALTER quota_route SET DEFAULT 5;`) - .raw(`ALTER TABLE organization ALTER quota_project SET DEFAULT 1;`) - .raw(`ALTER TABLE organization ALTER quota_group SET DEFAULT 10;`) - .raw(`ALTER TABLE organization ALTER quota_notification SET DEFAULT 10;`) - .raw(`ALTER TABLE organization ALTER quota_environment SET DEFAULT 5;`) - .alterTable('organization', (table) => { + .raw('ALTER TABLE organization ALTER quota_route SET DEFAULT 5;') + .raw('ALTER TABLE organization ALTER quota_project SET DEFAULT 1;') + .raw('ALTER TABLE organization ALTER quota_group SET DEFAULT 10;') + .raw('ALTER TABLE organization ALTER quota_notification SET DEFAULT 10;') + .raw('ALTER TABLE organization ALTER quota_environment SET DEFAULT 5;') + .alterTable('organization', (table) => { table.dropColumn('created'); - }) + }); }; diff --git a/services/api/database/migrations/20240507000000_delete_orphaned_backup_resources.js b/services/api/database/migrations/20240507000000_delete_orphaned_backup_resources.js index 37ad583535..17cdf05387 100644 --- a/services/api/database/migrations/20240507000000_delete_orphaned_backup_resources.js +++ b/services/api/database/migrations/20240507000000_delete_orphaned_backup_resources.js @@ -2,7 +2,7 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { // remove any deleted environment backups that are flagged as deleted // or where the environment/project are deleted or no longer exist return knex.schema @@ -15,15 +15,15 @@ exports.up = function(knex) { .alterTable('environment_backup', (table) => { table.dropColumn('deleted'); }); - }; +}; - /** +/** * @param { import("knex").Knex } knex * @returns { Promise } */ - exports.down = function(knex) { - return knex.schema +exports.down = function (knex) { + return knex.schema .alterTable('environment_backup', (table) => { - table.timestamp('deleted').notNullable().defaultTo('0000-00-00 00:00:00'); + table.timestamp('deleted').notNullable().defaultTo('0000-00-00 00:00:00'); }); - }; +}; diff --git a/services/api/database/migrations/20240512000000_sshkeyused.js b/services/api/database/migrations/20240512000000_sshkeyused.js index c4a36eb52d..a94f3f92e9 100644 --- a/services/api/database/migrations/20240512000000_sshkeyused.js +++ b/services/api/database/migrations/20240512000000_sshkeyused.js @@ -2,20 +2,20 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('ssh_key', (table) => { - table.datetime('last_used'); - }) + table.datetime('last_used'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('ssh_key', (table) => { - table.dropColumn('last_used'); - }) + table.dropColumn('last_used'); + }); }; diff --git a/services/api/database/migrations/20240630000000_sshkey_types.js b/services/api/database/migrations/20240630000000_sshkey_types.js index 8ef9b88d35..833125259f 100644 --- a/services/api/database/migrations/20240630000000_sshkey_types.js +++ b/services/api/database/migrations/20240630000000_sshkey_types.js @@ -2,40 +2,30 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema.table('ssh_key', (table) => { - table.string('key_type_new').notNullable().defaultTo('ssh-rsa'); - }).then(() => { - return knex('ssh_key').update({ - key_type_new: knex.ref('key_type') - }); - }).then(function () { - return knex.schema.table('ssh_key', (table) => { - table.dropColumn('key_type'); - table.renameColumn('key_type_new', 'key_type'); - }) - }); +exports.up = async function (knex) { + return knex.schema.table('ssh_key', (table) => { + table.string('key_type_new').notNullable().defaultTo('ssh-rsa'); + }).then(() => knex('ssh_key').update({ + key_type_new: knex.ref('key_type'), + })).then(() => knex.schema.table('ssh_key', (table) => { + table.dropColumn('key_type'); + table.renameColumn('key_type_new', 'key_type'); + })); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema.table('ssh_key', (table) => { - table.enu('key_type_new', ['ssh-rsa', 'ssh-ed25519','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521']).notNullable().defaultTo('ssh-rsa'); - }).then(() => { - return knex('ssh_key') - .whereNotIn('key_type', ['ssh-rsa', 'ssh-ed25519','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521']) - .del(); - }).then(function () { - return knex('ssh_key').update({ - key_type_new: knex.ref('key_type') - }); - }).then(function () { - return knex.schema.table('ssh_key', (table) => { - table.dropColumn('key_type'); - table.renameColumn('key_type_new', 'key_type'); - }) - }); +exports.down = async function (knex) { + return knex.schema.table('ssh_key', (table) => { + table.enu('key_type_new', ['ssh-rsa', 'ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521']).notNullable().defaultTo('ssh-rsa'); + }).then(() => knex('ssh_key') + .whereNotIn('key_type', ['ssh-rsa', 'ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521']) + .del()).then(() => knex('ssh_key').update({ + key_type_new: knex.ref('key_type'), + })).then(() => knex.schema.table('ssh_key', (table) => { + table.dropColumn('key_type'); + table.renameColumn('key_type_new', 'key_type'); + })); }; diff --git a/services/api/database/migrations/20241119031013_trim_sshkeys.js b/services/api/database/migrations/20241119031013_trim_sshkeys.js index 5ded1c0520..6c2f5994e4 100644 --- a/services/api/database/migrations/20241119031013_trim_sshkeys.js +++ b/services/api/database/migrations/20241119031013_trim_sshkeys.js @@ -2,17 +2,17 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex('ssh_key').update({ - key_value: knex.raw("REPLACE(key_value, '\n', '')") - }) + key_value: knex.raw("REPLACE(key_value, '\n', '')"), + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { +exports.down = function (knex) { // Nothing to do - return knex.schema + return knex.schema; }; diff --git a/services/api/database/migrations/20241229000000_user_last_accessed.js b/services/api/database/migrations/20241229000000_user_last_accessed.js index b1d65853ed..716c365ebe 100644 --- a/services/api/database/migrations/20241229000000_user_last_accessed.js +++ b/services/api/database/migrations/20241229000000_user_last_accessed.js @@ -2,25 +2,24 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - userTable = await knex.schema.hasTable('user'); - if (!userTable) { - return knex.schema - .createTable('user', function (table) { - table.specificType('usid', 'CHAR(36)'); - table.datetime('last_accessed'); - table.primary(['usid']); - }) - } - else { - return knex.schema - } +exports.up = async function (knex) { + userTable = await knex.schema.hasTable('user'); + if (!userTable) { + return knex.schema + .createTable('user', (table) => { + table.specificType('usid', 'CHAR(36)'); + table.datetime('last_accessed'); + table.primary(['usid']); + }); + } + + return knex.schema; }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema.dropTable('user'); +exports.down = async function (knex) { + return knex.schema.dropTable('user'); }; diff --git a/services/api/database/migrations/20250131000000_source_type_enums.js b/services/api/database/migrations/20250131000000_source_type_enums.js index ef2afea9bb..dee7495eda 100644 --- a/services/api/database/migrations/20250131000000_source_type_enums.js +++ b/services/api/database/migrations/20250131000000_source_type_enums.js @@ -1,22 +1,22 @@ -exports.up = function(knex) { - // fix the way the enum values are stored to be lowercase for mysql strict - return Promise.all([ - knex('deployment') - .where('source_type', '=', 'API') - .update('source_type', 'api'), - knex('deployment') - .where('source_type', '=', 'WEBHOOK') - .update('source_type', 'webhook'), - knex('task') - .where('source_type', '=', 'API') - .update('source_type', 'api'), - ]); - }; +exports.up = function (knex) { + // fix the way the enum values are stored to be lowercase for mysql strict + return Promise.all([ + knex('deployment') + .where('source_type', '=', 'API') + .update('source_type', 'api'), + knex('deployment') + .where('source_type', '=', 'WEBHOOK') + .update('source_type', 'webhook'), + knex('task') + .where('source_type', '=', 'API') + .update('source_type', 'api'), + ]); +}; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { -return knex.schema; -}; \ No newline at end of file +exports.down = function (knex) { + return knex.schema; +}; diff --git a/services/api/database/migrations/20250131000100_mysql8_compatibility.js b/services/api/database/migrations/20250131000100_mysql8_compatibility.js index da891923f4..41f25d8ad6 100644 --- a/services/api/database/migrations/20250131000100_mysql8_compatibility.js +++ b/services/api/database/migrations/20250131000100_mysql8_compatibility.js @@ -2,78 +2,78 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema - .alterTable('advanced_task_definition', function (table) { - table.specificType('description', 'text').notNullable().alter(); - table.string('group_name', 300).alter(); - table.specificType('command', 'text').alter(); - table.timestamp('deleted').notNullable().alter(); +exports.up = async function (knex) { + return knex.schema + .alterTable('advanced_task_definition', (table) => { + table.specificType('description', 'text').notNullable().alter(); + table.string('group_name', 300).alter(); + table.specificType('command', 'text').alter(); + table.timestamp('deleted').notNullable().alter(); }) - .alterTable('environment', function (table) { - table.timestamp('deleted').notNullable().alter(); - table.string('deploy_base_ref', 250).alter(); - table.string('deploy_head_ref', 250).alter(); + .alterTable('environment', (table) => { + table.timestamp('deleted').notNullable().alter(); + table.string('deploy_base_ref', 250).alter(); + table.string('deploy_head_ref', 250).alter(); }) - .alterTable('environment_fact', function (table) { - table.specificType('description', 'text').alter(); - table.specificType('category', 'text').alter(); + .alterTable('environment_fact', (table) => { + table.specificType('description', 'text').alter(); + table.specificType('category', 'text').alter(); }) - .alterTable('environment_problem', function (table) { - table.string('identifier', 100).notNullable().alter(); - table.string('lagoon_service', 100).defaultTo('').alter(); - table.specificType('description', 'text').alter(); - table.string('version', 100).defaultTo('').alter(); - table.timestamp('deleted').notNullable().alter(); + .alterTable('environment_problem', (table) => { + table.string('identifier', 100).notNullable().alter(); + table.string('lagoon_service', 100).defaultTo('').alter(); + table.specificType('description', 'text').alter(); + table.string('version', 100).defaultTo('').alter(); + table.timestamp('deleted').notNullable().alter(); }) - .alterTable('organization', function (table) { - table.specificType('description', 'text').notNullable().alter(); + .alterTable('organization', (table) => { + table.specificType('description', 'text').notNullable().alter(); }) - .alterTable('s3_file', function (table) { - table.timestamp('created').notNullable().defaultTo(knex.fn.now()).alter(); - table.timestamp('deleted').notNullable().alter(); - }) - .alterTable('workflow', function (table) { - table.timestamp('deleted').notNullable().alter(); + .alterTable('s3_file', (table) => { + table.timestamp('created').notNullable().defaultTo(knex.fn.now()).alter(); + table.timestamp('deleted').notNullable().alter(); }) + .alterTable('workflow', (table) => { + table.timestamp('deleted').notNullable().alter(); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - // caveats around this are that the rollback can only work while data is still saved in keycloak attributes - // once we remove that duplication of attribute into keycloak, this rollback would result in data loss for group>project associations - // for any group project associations made after the attribute removal - return knex.schema - .alterTable('advanced_task_definition', function (table) { - table.specificType('description', 'text').defaultTo('').notNullable().alter(); - table.string('group_name', 2000).alter(); - table.specificType('command', 'text').defaultTo('').alter(); - }) - .alterTable('environment', function (table) { - table.string('deploy_base_ref', 100).alter(); - table.string('deploy_head_ref', 100).alter(); +exports.down = async function (knex) { + // caveats around this are that the rollback can only work while data is still saved in keycloak attributes + // once we remove that duplication of attribute into keycloak, this rollback would result in data loss for group>project associations + // for any group project associations made after the attribute removal + return knex.schema + .alterTable('advanced_task_definition', (table) => { + table.specificType('description', 'text').defaultTo('').notNullable().alter(); + table.string('group_name', 2000).alter(); + table.specificType('command', 'text').defaultTo('').alter(); }) - .alterTable('environment_fact', function (table) { - table.specificType('description', 'text').defaultTo('').alter(); - table.specificType('category', 'text').defaultTo('').alter(); + .alterTable('environment', (table) => { + table.string('deploy_base_ref', 100).alter(); + table.string('deploy_head_ref', 100).alter(); }) - .alterTable('environment_problem', function (table) { - table.string('identifier', 300).notNullable().alter(); - table.string('lagoon_service', 300).defaultTo('').alter(); - table.specificType('description', 'text').defaultTo('').alter(); - table.string('version', 300).defaultTo('').alter(); + .alterTable('environment_fact', (table) => { + table.specificType('description', 'text').defaultTo('').alter(); + table.specificType('category', 'text').defaultTo('').alter(); }) - .alterTable('project', function (table) { - table.json('metadata').defaultTo('{}').alter(); + .alterTable('environment_problem', (table) => { + table.string('identifier', 300).notNullable().alter(); + table.string('lagoon_service', 300).defaultTo('').alter(); + table.specificType('description', 'text').defaultTo('').alter(); + table.string('version', 300).defaultTo('').alter(); }) - .alterTable('s3_file', function (table) { - table.datetime('created').notNullable().defaultTo(knex.fn.now()).alter(); - table.datetime('deleted').notNullable().defaultTo('0000-00-00 00:00:00').alter(); + .alterTable('project', (table) => { + table.json('metadata').defaultTo('{}').alter(); }) - .alterTable('organization', function (table) { - table.specificType('description', 'text').notNullable().defaultTo('').alter(); + .alterTable('s3_file', (table) => { + table.datetime('created').notNullable().defaultTo(knex.fn.now()).alter(); + table.datetime('deleted').notNullable().defaultTo('0000-00-00 00:00:00').alter(); }) + .alterTable('organization', (table) => { + table.specificType('description', 'text').notNullable().defaultTo('').alter(); + }); }; diff --git a/services/api/database/migrations/20250206000000_org_env_var.js b/services/api/database/migrations/20250206000000_org_env_var.js index b5530f23a6..e13411316e 100644 --- a/services/api/database/migrations/20250206000000_org_env_var.js +++ b/services/api/database/migrations/20250206000000_org_env_var.js @@ -2,23 +2,23 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { +exports.up = async function (knex) { return knex.schema - .alterTable('env_vars', (table) => { - table.integer('organization'); - table.unique(['name', 'organization'], {indexName: 'name_organization'}); - table.index('organization', 'organization_index'); - }) + .alterTable('env_vars', (table) => { + table.integer('organization'); + table.unique(['name', 'organization'], { indexName: 'name_organization' }); + table.index('organization', 'organization_index'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { +exports.down = async function (knex) { return knex.schema - .alterTable('env_vars', (table) => { + .alterTable('env_vars', (table) => { table.dropUnique(['name', 'organization'], 'name_organization'); table.dropColumn('organization'); - }) + }); }; diff --git a/services/api/database/migrations/20250218000000_audit_log.js b/services/api/database/migrations/20250218000000_audit_log.js index 3c549e8814..8f35adc98e 100644 --- a/services/api/database/migrations/20250218000000_audit_log.js +++ b/services/api/database/migrations/20250218000000_audit_log.js @@ -2,37 +2,36 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - organizations = await knex.schema.hasTable('audit_log'); - if (!organizations) { - return knex.schema - .createTable('audit_log', function (table) { - table.increments('id').notNullable().primary(); - table.specificType('usid', 'CHAR(36)'); // will be uuid from keycloak - table.string('email_address', 300); - table.specificType('resource_id', 'CHAR(36)'); // could be uuid (group/user) or int (project/env/org) - table.enu('resource_type', ['backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable','workflow']); - table.string('resource_details', 300); - table.specificType('linked_resource_id', 'CHAR(36)'); // could be uuid (group/user) or int (project/env/org) - table.enu('linked_resource_type', ['backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable','workflow']); - table.string('linked_resource_details', 300); - table.string('audit_event', 300); - table.specificType('impersonator_id', 'CHAR(36)'); // will be uuid from keycloak - table.string('impersonator_username', 300); - table.string('ip_address', 300); - table.enu('source', ['api','cli','ui']); - table.datetime('created').notNullable().defaultTo(knex.fn.now()); - }) - } - else { - return knex.schema - } +exports.up = async function (knex) { + organizations = await knex.schema.hasTable('audit_log'); + if (!organizations) { + return knex.schema + .createTable('audit_log', (table) => { + table.increments('id').notNullable().primary(); + table.specificType('usid', 'CHAR(36)'); // will be uuid from keycloak + table.string('email_address', 300); + table.specificType('resource_id', 'CHAR(36)'); // could be uuid (group/user) or int (project/env/org) + table.enu('resource_type', ['backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow']); + table.string('resource_details', 300); + table.specificType('linked_resource_id', 'CHAR(36)'); // could be uuid (group/user) or int (project/env/org) + table.enu('linked_resource_type', ['backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow']); + table.string('linked_resource_details', 300); + table.string('audit_event', 300); + table.specificType('impersonator_id', 'CHAR(36)'); // will be uuid from keycloak + table.string('impersonator_username', 300); + table.string('ip_address', 300); + table.enu('source', ['api', 'cli', 'ui']); + table.datetime('created').notNullable().defaultTo(knex.fn.now()); + }); + } + + return knex.schema; }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema; }; diff --git a/services/api/database/migrations/20250312183338_delete_workflows.js b/services/api/database/migrations/20250312183338_delete_workflows.js index 61b5b3c057..23ab167f0e 100644 --- a/services/api/database/migrations/20250312183338_delete_workflows.js +++ b/services/api/database/migrations/20250312183338_delete_workflows.js @@ -2,34 +2,34 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { - return knex.schema - .raw(`DELETE FROM audit_log +exports.up = function (knex) { + return knex.schema + .raw(`DELETE FROM audit_log WHERE resource_type = 'workflow' OR linked_resource_type = 'workflow'`) - .raw(`ALTER TABLE audit_log + .raw(`ALTER TABLE audit_log MODIFY resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable');`) - .raw(`ALTER TABLE audit_log + .raw(`ALTER TABLE audit_log MODIFY linked_resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable');`) - .dropTable('workflow'); + .dropTable('workflow'); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { - return knex.schema +exports.down = function (knex) { + return knex.schema .raw(`ALTER TABLE audit_log MODIFY resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow');`) .raw(`ALTER TABLE audit_log MODIFY linked_resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow');`) - .createTable('workflow', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 50).notNullable(); - table.string('event', 300).notNullable(); - table.integer('project', 11).notNullable(); - table.integer('advanced_task_definition', 11).notNullable(); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.timestamp('deleted').notNullable(); + .createTable('workflow', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 50).notNullable(); + table.string('event', 300).notNullable(); + table.integer('project', 11).notNullable(); + table.integer('advanced_task_definition', 11).notNullable(); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.timestamp('deleted').notNullable(); }); }; diff --git a/services/api/database/migrations/20250415000000_project_pattern_deprecation.js b/services/api/database/migrations/20250415000000_project_pattern_deprecation.js index 0c592d0752..1a26be4756 100644 --- a/services/api/database/migrations/20250415000000_project_pattern_deprecation.js +++ b/services/api/database/migrations/20250415000000_project_pattern_deprecation.js @@ -2,32 +2,32 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex.schema - .alterTable('project', (table) => { - table.dropColumn('openshift_project_pattern'); - }) - .alterTable('environment', (table) => { - table.dropColumn('openshift_project_pattern'); - }) - .alterTable('deploy_target_config', (table) => { - table.dropColumn('deploy_target_project_pattern'); - }); + .alterTable('project', (table) => { + table.dropColumn('openshift_project_pattern'); + }) + .alterTable('environment', (table) => { + table.dropColumn('openshift_project_pattern'); + }) + .alterTable('deploy_target_config', (table) => { + table.dropColumn('deploy_target_project_pattern'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { +exports.down = function (knex) { return knex.schema - .alterTable('project', (table) => { - table.string('openshift_project_pattern', 300); - }) - .alterTable('environment', (table) => { - table.string('openshift_project_pattern', 300); - }) - .alterTable('deploy_target_config', (table) => { - table.string('deploy_target_project_pattern', 300); - }); + .alterTable('project', (table) => { + table.string('openshift_project_pattern', 300); + }) + .alterTable('environment', (table) => { + table.string('openshift_project_pattern', 300); + }) + .alterTable('deploy_target_config', (table) => { + table.string('deploy_target_project_pattern', 300); + }); }; diff --git a/services/api/database/migrations/20250520225815_add_emailoptout_to_user.js b/services/api/database/migrations/20250520225815_add_emailoptout_to_user.js index bef06d5802..8fb67939a5 100644 --- a/services/api/database/migrations/20250520225815_add_emailoptout_to_user.js +++ b/services/api/database/migrations/20250520225815_add_emailoptout_to_user.js @@ -2,8 +2,8 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { - return knex.schema.alterTable('user', function(table) { +exports.up = function (knex) { + return knex.schema.alterTable('user', (table) => { // table.boolean('org_email_optin').notNullable().defaultTo(true); table.boolean('opt_email_org_role').notNullable().defaultTo(true); // when a users role within the organization management changes table.boolean('opt_email_sshkey').notNullable().defaultTo(true); // when an ssh key is added or removed from the user @@ -15,8 +15,8 @@ exports.up = function(knex) { * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { - return knex.schema.alterTable('user', function(table) { +exports.down = function (knex) { + return knex.schema.alterTable('user', (table) => { // table.dropColumn('org_email_optin'); table.dropColumn('opt_email_org_role'); table.dropColumn('opt_email_sshkey'); diff --git a/services/api/database/migrations/20250619040511_add_file_to_auditlog_type.js b/services/api/database/migrations/20250619040511_add_file_to_auditlog_type.js index 62e9740ea8..33a1988fe7 100644 --- a/services/api/database/migrations/20250619040511_add_file_to_auditlog_type.js +++ b/services/api/database/migrations/20250619040511_add_file_to_auditlog_type.js @@ -2,19 +2,18 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { - return knex.schema. - raw(`ALTER TABLE audit_log MODIFY resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow', 'file');`). - raw(`ALTER TABLE audit_log MODIFY linked_resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow', 'file');`); +exports.up = function (knex) { + return knex.schema + .raw('ALTER TABLE audit_log MODIFY resource_type ENUM(\'backup\', \'bulkdeployment\', \'deployment\', \'deploytarget\', \'deploytargetconfig\', \'environment\', \'group\', \'notification\', \'organization\', \'project\', \'sshkey\', \'task\', \'user\', \'variable\', \'workflow\', \'file\');') + .raw('ALTER TABLE audit_log MODIFY linked_resource_type ENUM(\'backup\', \'bulkdeployment\', \'deployment\', \'deploytarget\', \'deploytargetconfig\', \'environment\', \'group\', \'notification\', \'organization\', \'project\', \'sshkey\', \'task\', \'user\', \'variable\', \'workflow\', \'file\');'); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { - return knex.schema. - raw(`ALTER TABLE audit_log MODIFY resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow');`). - raw(`ALTER TABLE audit_log MODIFY linked_resource_type ENUM('backup', 'bulkdeployment', 'deployment', 'deploytarget', 'deploytargetconfig', 'environment', 'group', 'notification', 'organization', 'project', 'sshkey', 'task', 'user', 'variable', 'workflow');`); - +exports.down = function (knex) { + return knex.schema + .raw('ALTER TABLE audit_log MODIFY resource_type ENUM(\'backup\', \'bulkdeployment\', \'deployment\', \'deploytarget\', \'deploytargetconfig\', \'environment\', \'group\', \'notification\', \'organization\', \'project\', \'sshkey\', \'task\', \'user\', \'variable\', \'workflow\');') + .raw('ALTER TABLE audit_log MODIFY linked_resource_type ENUM(\'backup\', \'bulkdeployment\', \'deployment\', \'deploytarget\', \'deploytargetconfig\', \'environment\', \'group\', \'notification\', \'organization\', \'project\', \'sshkey\', \'task\', \'user\', \'variable\', \'workflow\');'); }; diff --git a/services/api/database/migrations/20250703000000_retention_policy.js b/services/api/database/migrations/20250703000000_retention_policy.js index 0b9e73ecdd..213f919f9a 100644 --- a/services/api/database/migrations/20250703000000_retention_policy.js +++ b/services/api/database/migrations/20250703000000_retention_policy.js @@ -2,31 +2,31 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema - .createTable('retention_policy', function (table) { - table.increments('id').notNullable().primary(); - table.string('name', 300); - table.enu('type',['harbor','history']).notNullable(); - table.text('configuration'); - table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.unique(['name', 'type'], {indexName: 'retention_policy'}); - }) - .createTable('retention_policy_reference', function (table) { - table.integer('retention_policy'); - table.enu('scope',['global','organization','project']).notNullable(); - table.integer('id'); - table.unique(['retention_policy', 'scope', 'id'], {indexName: 'organization_policy'}); +exports.up = async function (knex) { + return knex.schema + .createTable('retention_policy', (table) => { + table.increments('id').notNullable().primary(); + table.string('name', 300); + table.enu('type', ['harbor', 'history']).notNullable(); + table.text('configuration'); + table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.unique(['name', 'type'], { indexName: 'retention_policy' }); }) + .createTable('retention_policy_reference', (table) => { + table.integer('retention_policy'); + table.enu('scope', ['global', 'organization', 'project']).notNullable(); + table.integer('id'); + table.unique(['retention_policy', 'scope', 'id'], { indexName: 'organization_policy' }); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .dropTable('retention_policy') - .dropTable('retention_policy_reference') -}; \ No newline at end of file + .dropTable('retention_policy_reference'); +}; diff --git a/services/api/database/migrations/20250822070204_delete_internal_container_registry_scoped_env_vars.js b/services/api/database/migrations/20250822070204_delete_internal_container_registry_scoped_env_vars.js index effab740d6..166c92c88c 100644 --- a/services/api/database/migrations/20250822070204_delete_internal_container_registry_scoped_env_vars.js +++ b/services/api/database/migrations/20250822070204_delete_internal_container_registry_scoped_env_vars.js @@ -2,10 +2,10 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex.schema - .raw(`DELETE FROM env_vars WHERE scope = 'internal_container_registry'`) - .raw(`ALTER TABLE env_vars + .raw('DELETE FROM env_vars WHERE scope = \'internal_container_registry\'') + .raw(`ALTER TABLE env_vars MODIFY scope ENUM('global', 'build', 'runtime', 'container_registry') NOT NULL DEFAULT 'global';`); }; @@ -13,8 +13,8 @@ exports.up = function(knex) { * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { +exports.down = function (knex) { return knex.schema - .raw(`ALTER TABLE env_vars + .raw(`ALTER TABLE env_vars MODIFY scope ENUM('global', 'build', 'runtime', 'container_registry', 'internal_container_registry') NOT NULL DEFAULT 'global';`); -}; \ No newline at end of file +}; diff --git a/services/api/database/migrations/20250826000000_org_audit_log.js b/services/api/database/migrations/20250826000000_org_audit_log.js index bcb8c79f50..3c0e60c029 100644 --- a/services/api/database/migrations/20250826000000_org_audit_log.js +++ b/services/api/database/migrations/20250826000000_org_audit_log.js @@ -2,21 +2,21 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { +exports.up = async function (knex) { return knex.schema - .alterTable('audit_log', (table) => { - table.integer('organization_id'); - table.index('organization_id', 'organization_index'); - }) + .alterTable('audit_log', (table) => { + table.integer('organization_id'); + table.index('organization_id', 'organization_index'); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { +exports.down = async function (knex) { return knex.schema - .alterTable('audit_log', (table) => { - table.dropColumn('organization_id'); - }) + .alterTable('audit_log', (table) => { + table.dropColumn('organization_id'); + }); }; diff --git a/services/api/database/migrations/20251019203621_add_updated_to_env_vars.js b/services/api/database/migrations/20251019203621_add_updated_to_env_vars.js index 79640724ea..5d0396b2b7 100644 --- a/services/api/database/migrations/20251019203621_add_updated_to_env_vars.js +++ b/services/api/database/migrations/20251019203621_add_updated_to_env_vars.js @@ -2,12 +2,12 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex.schema - .alterTable('env_vars', (table) => { - table.datetime('updated').notNullable().defaultTo(knex.fn.now()); - }) - .raw("UPDATE env_vars SET updated='1970-01-01 00:00:00'"); + .alterTable('env_vars', (table) => { + table.datetime('updated').notNullable().defaultTo(knex.fn.now()); + }) + .raw("UPDATE env_vars SET updated='1970-01-01 00:00:00'"); // Note, we do the above update so that all _existing_ env vars are // not picked up as needing to be deployed (since we're using 'updated' to track new/updated vars) // but any newly created items will get the _current_ date/time @@ -17,9 +17,9 @@ exports.up = function(knex) { * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { -return knex.schema - .alterTable('env_vars', (table) => { - table.dropColumn('updated'); - }) +exports.down = function (knex) { + return knex.schema + .alterTable('env_vars', (table) => { + table.dropColumn('updated'); + }); }; diff --git a/services/api/database/migrations/20251030000000_routes.js b/services/api/database/migrations/20251030000000_routes.js index 93181e8606..0b47d2e56d 100644 --- a/services/api/database/migrations/20251030000000_routes.js +++ b/services/api/database/migrations/20251030000000_routes.js @@ -2,85 +2,84 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - const route = await knex.schema.hasTable('routes'); - if (!route) { - return knex.schema - .createTable('routes', function (table) { - table.increments('id').notNullable().primary(); - table.timestamp('created').notNullable().defaultTo(knex.fn.now()); - table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); - table.string('domain', 253).notNullable(); - table.integer('project').notNullable(); - table.integer('environment'); - table.string('service', 300); - table.boolean('tls_acme').notNullable().defaultTo(1); // default to true - table.enu('insecure', ['Allow', 'Redirect', 'None']).notNullable().defaultTo('Redirect'); - table.string('monitoring_path', 300); - table.boolean('hsts_enabled').notNullable().defaultTo(0); // default to false - table.boolean('hsts_include_subdomains').notNullable().defaultTo(0); - table.boolean('hsts_preload').notNullable().defaultTo(0); // default to false - table.integer('hsts_max_age').defaultTo(3153600); - table.boolean('primary').notNullable().defaultTo(0); // default to false - table.boolean('disable_request_verification').notNullable().defaultTo(0); // default to false - table.text('path_routes'); - table.enu('source', ['api','yaml','autogenerated']).notNullable().defaultTo('api'); - table.enu('type', ['standard', 'active', 'standby']).notNullable().defaultTo('standard'); - // table.boolean('wildcard').notNullable().defaultTo(0); // TBD - // table.boolean('wildcard_apex').notNullable().defaultTo(0); // TBD - // table.enu('verified', ['new','pending', 'running', 'error', 'verified']).notNullable().defaultTo('new'); // TBD - // table.json('verification').defaultTo('{}'); //TBD - // table.string('ingress_class', 300); // TBD - table.unique(['domain', 'project'], {indexName: 'route_project'}); - }) - .createTable('routes_alternate_domain', function (table) { - table.increments('id').notNullable().primary(); - table.integer('route_id'); //route id - table.string('domain', 253).notNullable(); - // table.json('verification').defaultTo('{}'); //TBD - table.unique(['domain', 'route_id'], {indexName: 'alternate_domain_route'}); - }) - .createTable('routes_annotations', function (table) { - table.increments('id').notNullable().primary(); - table.integer('route_id'); - table.string('key', 63).notNullable(); - table.text('value'); - table.unique(['key', 'route_id'], {indexName: 'alternate_domain_route'}); - }) - .createTable('routes_autogenerated_configuration', (table) => { - table.increments('id').notNullable().primary(); - table.enu('type', ['project', 'environment']); - table.integer('type_id'); - table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); - table.boolean('enabled').notNullable().defaultTo(1); // default to true - table.boolean('allow_pull_requests').notNullable().defaultTo(1); // default to true - table.boolean('tls_acme').notNullable().defaultTo(1); // default to true - table.enu('insecure', ['Allow', 'Redirect', 'None']).notNullable().defaultTo('Redirect'); - table.text('prefixes') // would be comma separated list of prefixes representing what is in the .lagoon.yml, would override lagoon.yml if defined - table.text('path_routes') // would be JSON equivalent of what is in lagoon.yml, would override lagoon.yml if defined - table.boolean('disable_request_verification').notNullable().defaultTo(0); // default to false - table.unique(['type', 'type_id'], {indexName: 'autogenerated_route_config_type'}); - }) - .alterTable('organization', function (table) { - table.boolean('feature_api_routes').defaultTo(0); // disable api routes feature by default, future release of lagoon will remove this when routes api becomes generally available - }) - } - else { - return knex.schema - } +exports.up = async function (knex) { + const route = await knex.schema.hasTable('routes'); + if (!route) { + return knex.schema + .createTable('routes', (table) => { + table.increments('id').notNullable().primary(); + table.timestamp('created').notNullable().defaultTo(knex.fn.now()); + table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); + table.string('domain', 253).notNullable(); + table.integer('project').notNullable(); + table.integer('environment'); + table.string('service', 300); + table.boolean('tls_acme').notNullable().defaultTo(1); // default to true + table.enu('insecure', ['Allow', 'Redirect', 'None']).notNullable().defaultTo('Redirect'); + table.string('monitoring_path', 300); + table.boolean('hsts_enabled').notNullable().defaultTo(0); // default to false + table.boolean('hsts_include_subdomains').notNullable().defaultTo(0); + table.boolean('hsts_preload').notNullable().defaultTo(0); // default to false + table.integer('hsts_max_age').defaultTo(3153600); + table.boolean('primary').notNullable().defaultTo(0); // default to false + table.boolean('disable_request_verification').notNullable().defaultTo(0); // default to false + table.text('path_routes'); + table.enu('source', ['api', 'yaml', 'autogenerated']).notNullable().defaultTo('api'); + table.enu('type', ['standard', 'active', 'standby']).notNullable().defaultTo('standard'); + // table.boolean('wildcard').notNullable().defaultTo(0); // TBD + // table.boolean('wildcard_apex').notNullable().defaultTo(0); // TBD + // table.enu('verified', ['new','pending', 'running', 'error', 'verified']).notNullable().defaultTo('new'); // TBD + // table.json('verification').defaultTo('{}'); //TBD + // table.string('ingress_class', 300); // TBD + table.unique(['domain', 'project'], { indexName: 'route_project' }); + }) + .createTable('routes_alternate_domain', (table) => { + table.increments('id').notNullable().primary(); + table.integer('route_id'); // route id + table.string('domain', 253).notNullable(); + // table.json('verification').defaultTo('{}'); //TBD + table.unique(['domain', 'route_id'], { indexName: 'alternate_domain_route' }); + }) + .createTable('routes_annotations', (table) => { + table.increments('id').notNullable().primary(); + table.integer('route_id'); + table.string('key', 63).notNullable(); + table.text('value'); + table.unique(['key', 'route_id'], { indexName: 'alternate_domain_route' }); + }) + .createTable('routes_autogenerated_configuration', (table) => { + table.increments('id').notNullable().primary(); + table.enu('type', ['project', 'environment']); + table.integer('type_id'); + table.timestamp('updated').notNullable().defaultTo(knex.fn.now()); + table.boolean('enabled').notNullable().defaultTo(1); // default to true + table.boolean('allow_pull_requests').notNullable().defaultTo(1); // default to true + table.boolean('tls_acme').notNullable().defaultTo(1); // default to true + table.enu('insecure', ['Allow', 'Redirect', 'None']).notNullable().defaultTo('Redirect'); + table.text('prefixes'); // would be comma separated list of prefixes representing what is in the .lagoon.yml, would override lagoon.yml if defined + table.text('path_routes'); // would be JSON equivalent of what is in lagoon.yml, would override lagoon.yml if defined + table.boolean('disable_request_verification').notNullable().defaultTo(0); // default to false + table.unique(['type', 'type_id'], { indexName: 'autogenerated_route_config_type' }); + }) + .alterTable('organization', (table) => { + table.boolean('feature_api_routes').defaultTo(0); // disable api routes feature by default, future release of lagoon will remove this when routes api becomes generally available + }); + } + + return knex.schema; }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .dropTable('routes') .dropTable('routes_alternate_domain') .dropTable('routes_annotations') .dropTable('routes_autogenerated_configuration') .alterTable('organization', (table) => { - table.dropColumn('feature_api_routes'); + table.dropColumn('feature_api_routes'); }); -}; \ No newline at end of file +}; diff --git a/services/api/database/migrations/20251112052711_set_default_metadata.js b/services/api/database/migrations/20251112052711_set_default_metadata.js index 4a6292da42..77dad93915 100644 --- a/services/api/database/migrations/20251112052711_set_default_metadata.js +++ b/services/api/database/migrations/20251112052711_set_default_metadata.js @@ -4,18 +4,18 @@ */ // Need to utilize raw here instead of alterTable as MySQL requires // default values for JSON columns to be written as an expression -exports.up = async function(knex) { +exports.up = async function (knex) { return knex.schema - .raw(`UPDATE project SET metadata = '{}' WHERE metadata IS NULL;`) - .raw(`ALTER TABLE project MODIFY metadata JSON NOT NULL DEFAULT ('{}');`); + .raw('UPDATE project SET metadata = \'{}\' WHERE metadata IS NULL;') + .raw('ALTER TABLE project MODIFY metadata JSON NOT NULL DEFAULT (\'{}\');'); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { +exports.down = async function (knex) { return knex.schema - .raw(`ALTER TABLE project MODIFY metadata JSON NULL;`) - .raw(`UPDATE project SET metadata = NULL WHERE metadata = '{}';`); -}; \ No newline at end of file + .raw('ALTER TABLE project MODIFY metadata JSON NULL;') + .raw('UPDATE project SET metadata = NULL WHERE metadata = \'{}\';'); +}; diff --git a/services/api/database/migrations/20251112200000_remove_task_files.js b/services/api/database/migrations/20251112200000_remove_task_files.js index 39356e1b6d..24b153d494 100644 --- a/services/api/database/migrations/20251112200000_remove_task_files.js +++ b/services/api/database/migrations/20251112200000_remove_task_files.js @@ -2,28 +2,28 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = function(knex) { +exports.up = function (knex) { return knex.schema - .dropTable('s3_file') - .dropTable('task_file') + .dropTable('s3_file') + .dropTable('task_file'); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = function(knex) { -return knex.schema - .createTable('s3_file', function (table) { +exports.down = function (knex) { + return knex.schema + .createTable('s3_file', (table) => { table.increments('id').notNullable().primary(); table.string('filename', 100).notNullable(); table.text('s3_key').notNullable(); table.timestamp('created').notNullable().defaultTo(knex.fn.now()); table.timestamp('deleted').notNullable(); - }) - .createTable('task_file', function (table) { + }) + .createTable('task_file', (table) => { table.integer('tid'); table.integer('fid'); table.primary(['tid', 'fid']); - }) + }); }; diff --git a/services/api/database/migrations/20251115000000_buildtype.js b/services/api/database/migrations/20251115000000_buildtype.js index a286fa2085..5a7686e97e 100644 --- a/services/api/database/migrations/20251115000000_buildtype.js +++ b/services/api/database/migrations/20251115000000_buildtype.js @@ -2,10 +2,10 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ - exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('deployment', (table) => { - table.enu('build_type',['build','variables']).notNullable().defaultTo('build');; + table.enu('build_type', ['build', 'variables']).notNullable().defaultTo('build'); }) // set all existing deployments to build .raw("UPDATE deployment SET build_type='build'"); @@ -15,10 +15,10 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - // cant alter enums in place, so drop the column first :D - return knex.schema +exports.down = async function (knex) { + // cant alter enums in place, so drop the column first :D + return knex.schema .alterTable('deployment', (table) => { - table.dropColumn('build_type'); - }) + table.dropColumn('build_type'); + }); }; diff --git a/services/api/database/migrations/20251120000000_environment_idling_status.js b/services/api/database/migrations/20251120000000_environment_idling_status.js index 4dc618fb27..3c90c460d2 100644 --- a/services/api/database/migrations/20251120000000_environment_idling_status.js +++ b/services/api/database/migrations/20251120000000_environment_idling_status.js @@ -2,26 +2,26 @@ * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.up = async function(knex) { - return knex.schema +exports.up = async function (knex) { + return knex.schema .alterTable('environment', (table) => { - table.string('idle_state').notNullable().defaultTo('active'); - }) - .alterTable('environment_service', function (table) { - table.integer('replicas').notNullable().defaultTo(0); + table.string('idle_state').notNullable().defaultTo('active'); }) + .alterTable('environment_service', (table) => { + table.integer('replicas').notNullable().defaultTo(0); + }); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ -exports.down = async function(knex) { - return knex.schema +exports.down = async function (knex) { + return knex.schema .alterTable('environment', (table) => { - table.dropColumn('idle_state'); + table.dropColumn('idle_state'); }) .alterTable('environment_service', (table) => { - table.dropColumn('replicas'); - }) + table.dropColumn('replicas'); + }); }; diff --git a/services/api/jest.config.js b/services/api/jest.config.js index 2b6e4d1958..9375006a87 100644 --- a/services/api/jest.config.js +++ b/services/api/jest.config.js @@ -1,6 +1,28 @@ module.exports = { roots: ['./src'], + testEnvironment: 'node', transform: { - '^.+\\.tsx?$': 'ts-jest' - } + '^.+\.tsx?$': ['ts-jest', { tsconfig: './tsconfig.json' }], + }, + testPathIgnorePatterns: ['node_modules'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + // mariadb 3.x and node-fetch 3.x ship as ESM which Jest (CJS mode) cannot parse. + // redis v3 creates a live connection on import, keeping the test runner open. + // Redirect all three to manual mocks in __mocks__/. + moduleNameMapper: { + '^mariadb(/.*)?$': '/__mocks__/mariadb.js', + '^node-fetch$': '/__mocks__/node-fetch.js', + '^redis$': '/__mocks__/redis.js', + }, + collectCoverageFrom: [ + 'src/**/*.{ts,tsx,js}', + '!src/**/*.test.{ts,tsx,js}', + '!src/**/__tests__/**', + '!src/**/__snapshots__/**', + '!src/mocks.js', + '!src/typeDefs.js', + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'clover'], }; + diff --git a/services/api/package.json b/services/api/package.json index 31b9e7b8b4..2c65e4f8ac 100644 --- a/services/api/package.json +++ b/services/api/package.json @@ -1,18 +1,19 @@ { "name": "api", "version": "3.0.0", + "packageManager": "pnpm@10.30.1", "description": "Lagoon GraphQL API", "main": "dist/index.js", "scripts": { "format": "prettier-eslint --write '**/*.js'", - "lint": "../../node-packages/eslint-config-lagoon-node/node_modules/.bin/eslint .", + "lint": "eslint .", "test": "jest --forceExit --detectOpenHandles --maxWorkers=10", "test:watch": "jest --forceExit --detectOpenHandles --maxWorkers=10 --watch", - "build": "tsc --build", + "build": "tsc", "sync:gitlab:users": "node dist/gitlab-sync/users", "sync:gitlab:groups": "node dist/gitlab-sync/groups", "sync:gitlab:projects": "node dist/gitlab-sync/projects", - "sync:gitlab:all": "yarn run sync:gitlab:users && yarn run sync:gitlab:groups && yarn run sync:gitlab:projects", + "sync:gitlab:all": "pnpm run sync:gitlab:users && pnpm run sync:gitlab:groups && pnpm run sync:gitlab:projects", "migrations:lagoon": "node dist/migrations/lagoon/migrations.js" }, "keywords": [], @@ -25,8 +26,8 @@ "@aws-sdk/s3-request-presigner": "^3.828.0", "@graphql-tools/graphql-file-loader": "^8.0.19", "@graphql-tools/load": "^8.0.19", + "@graphql-tools/mock": "^8.7.20", "@graphql-tools/schema": "^10.0.23", - "@lagoon/commons": "4.0.0", "@s3pweb/keycloak-admin-client-cjs": "26.2.4", "@supercharge/request-ip": "^1.1.2", "@types/node-cache": "^4.2.5", @@ -34,6 +35,7 @@ "amqplib": "^0.10.7", "apollo-server-express": "^3.13.0", "aws-sdk": "^2.378.0", + "axios": "^1.12.0", "body-parser": "^1.18.2", "camelcase-keys": "^4.2.0", "compression": "^1.7.1", @@ -55,32 +57,54 @@ "jsonwebtoken": "^8.0.1", "keycloak-connect": "^26.1.1", "knex": "^3.0.1", + "lokka": "^1.7.0", "mariadb": "^3.4.5", "moment": "^2.24.0", "morgan": "^1.9.0", "mysql2": "^3.15.3", "newrelic": "^12.5.0", - "ramda": "0.25.0", + "node-fetch": "^3.3.2", + "ramda": "0.32.0", "redis": "^3.0.2", + "sha1": "^1.1.1", "snakecase-keys": "^1.2.0", "uuid4": "^1.0.0", "validator": "^13.12.0", "winston": "^3", - "winston-transport": "^4.4.0" + "winston-transport": "^4.4.0", + "ws": "^8.19.0" }, "devDependencies": { "@faker-js/faker": "^8.4.1", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", + "@types/amqp-connection-manager": "^2.0.12", "@types/express": "^4.17.6", "@types/jest": "^29.5.12", "@types/jsonwebtoken": "^9.0.8", - "@types/ramda": "types/npm-ramda#dist", + "@types/ramda": "^0.31.1", "@types/redis": "^2.8.32", + "@types/sha1": "^1.1.5", "@types/ws": "^8.18.0", + "eslint": "^8.57.1", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.32.0", "jest": "^29.7.0", "prettier": "^3.2.5", "prettier-eslint-cli": "^8.0.1", "ts-jest": "^29.1.2", "tsc-watch": "^6.0.4", "typescript": "^5.3.3" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "@newrelic/fn-inspect", + "@newrelic/native-metrics", + "@types/ramda", + "aws-sdk", + "core-js", + "protobufjs", + "@apollo/protobufjs" + ] } } diff --git a/services/api/pnpm-lock.yaml b/services/api/pnpm-lock.yaml new file mode 100644 index 0000000000..30356988cd --- /dev/null +++ b/services/api/pnpm-lock.yaml @@ -0,0 +1,10583 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@aws-sdk/client-s3': + specifier: ^3.828.0 + version: 3.997.0 + '@aws-sdk/lib-storage': + specifier: ^3.837.0 + version: 3.997.0(@aws-sdk/client-s3@3.997.0) + '@aws-sdk/s3-presigned-post': + specifier: ^3.929.0 + version: 3.997.0 + '@aws-sdk/s3-request-presigner': + specifier: ^3.828.0 + version: 3.997.0 + '@graphql-tools/graphql-file-loader': + specifier: ^8.0.19 + version: 8.1.9(graphql@16.13.0) + '@graphql-tools/load': + specifier: ^8.0.19 + version: 8.1.8(graphql@16.13.0) + '@graphql-tools/mock': + specifier: ^8.7.20 + version: 8.7.20(graphql@16.13.0) + '@graphql-tools/schema': + specifier: ^10.0.23 + version: 10.0.31(graphql@16.13.0) + '@s3pweb/keycloak-admin-client-cjs': + specifier: 26.2.4 + version: 26.2.4 + '@supercharge/request-ip': + specifier: ^1.1.2 + version: 1.2.0 + '@types/node-cache': + specifier: ^4.2.5 + version: 4.2.5 + amqp-connection-manager: + specifier: ^4.1.14 + version: 4.1.15(amqplib@0.10.9) + amqplib: + specifier: ^0.10.7 + version: 0.10.9 + apollo-server-express: + specifier: ^3.13.0 + version: 3.13.0(express@4.22.1)(graphql@16.13.0) + aws-sdk: + specifier: ^2.378.0 + version: 2.1693.0 + axios: + specifier: ^1.12.0 + version: 1.13.5 + body-parser: + specifier: ^1.18.2 + version: 1.20.4 + camelcase-keys: + specifier: ^4.2.0 + version: 4.2.0 + compression: + specifier: ^1.7.1 + version: 1.8.1 + cors: + specifier: ^2.8.4 + version: 2.8.6 + dotenv: + specifier: ^4.0.0 + version: 4.0.0 + dotenv-extended: + specifier: ^2.0.1 + version: 2.9.0 + elasticsearch: + specifier: ^15.0.0 + version: 15.5.0 + express: + specifier: ^4.16.1 + version: 4.22.1 + git-url-parse: + specifier: ^13.1.0 + version: 13.1.1 + graphql: + specifier: ^16.10.0 + version: 16.13.0 + graphql-iso-date: + specifier: ^3.5.0 + version: 3.6.1(graphql@16.13.0) + graphql-redis-subscriptions: + specifier: ^2.7.0 + version: 2.7.0(graphql-subscriptions@1.2.1(graphql@16.13.0)) + graphql-sse: + specifier: ^2.5.4 + version: 2.6.0(graphql@16.13.0) + graphql-subscriptions: + specifier: ^1.0.0 + version: 1.2.1(graphql@16.13.0) + graphql-tools: + specifier: ^9.0.18 + version: 9.0.26(graphql-ws@5.16.0(graphql@16.13.0))(graphql@16.13.0)(rxjs@7.8.2) + graphql-type-json: + specifier: ^0.3.0 + version: 0.3.2(graphql@16.13.0) + graphql-upload: + specifier: ^17.0.0 + version: 17.0.0(@types/express@4.17.25)(graphql@16.13.0) + graphql-ws: + specifier: 5.16.0 + version: 5.16.0(graphql@16.13.0) + jsonwebtoken: + specifier: ^8.0.1 + version: 8.5.1 + keycloak-connect: + specifier: ^26.1.1 + version: 26.1.1 + knex: + specifier: ^3.0.1 + version: 3.1.0(mysql2@3.18.0(@types/node@25.3.0)) + lokka: + specifier: ^1.7.0 + version: 1.7.0 + mariadb: + specifier: ^3.4.5 + version: 3.5.1(@types/node@25.3.0) + moment: + specifier: ^2.24.0 + version: 2.30.1 + morgan: + specifier: ^1.9.0 + version: 1.10.1 + mysql2: + specifier: ^3.15.3 + version: 3.18.0(@types/node@25.3.0) + newrelic: + specifier: ^12.5.0 + version: 12.25.1 + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 + ramda: + specifier: 0.32.0 + version: 0.32.0 + redis: + specifier: ^3.0.2 + version: 3.1.2 + sha1: + specifier: ^1.1.1 + version: 1.1.1 + snakecase-keys: + specifier: ^1.2.0 + version: 1.2.0 + uuid4: + specifier: ^1.0.0 + version: 1.1.4 + validator: + specifier: ^13.12.0 + version: 13.15.26 + winston: + specifier: ^3 + version: 3.19.0 + winston-transport: + specifier: ^4.4.0 + version: 4.9.0 + ws: + specifier: ^8.19.0 + version: 8.19.0 + devDependencies: + '@faker-js/faker': + specifier: ^8.4.1 + version: 8.4.1 + '@types/amqp-connection-manager': + specifier: ^2.0.12 + version: 2.0.12 + '@types/express': + specifier: ^4.17.6 + version: 4.17.25 + '@types/jest': + specifier: ^29.5.12 + version: 29.5.14 + '@types/jsonwebtoken': + specifier: ^9.0.8 + version: 9.0.10 + '@types/ramda': + specifier: ^0.31.1 + version: 0.31.1 + '@types/redis': + specifier: ^2.8.32 + version: 2.8.32 + '@types/sha1': + specifier: ^1.1.5 + version: 1.1.5 + '@types/ws': + specifier: ^8.18.0 + version: 8.18.1 + '@typescript-eslint/eslint-plugin': + specifier: ^8.56.1 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.56.1 + version: 8.56.1(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.57.1 + version: 8.57.1 + eslint-config-airbnb-base: + specifier: ^15.0.0 + version: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: + specifier: ^2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@25.3.0) + prettier: + specifier: ^3.2.5 + version: 3.8.1 + prettier-eslint-cli: + specifier: ^8.0.1 + version: 8.0.1(prettier-eslint@16.4.2(typescript@5.9.3))(typescript@5.9.3) + ts-jest: + specifier: ^29.1.2 + version: 29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@25.3.0))(typescript@5.9.3) + tsc-watch: + specifier: ^6.0.4 + version: 6.3.1(typescript@5.9.3) + typescript: + specifier: ^5.3.3 + version: 5.9.3 + +packages: + + '@apollo/client@4.0.13': + resolution: {integrity: sha512-ziUPddxVZ0dg+/l61rFymkPFesENVb3P/a8hKtN1XyawTcydeyRwooM4xBXpakKbt2gxwlm5dvrE1AWEcQlK3g==} + peerDependencies: + graphql: ^16.0.0 + graphql-ws: ^5.5.5 || ^6.0.3 + react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc + react-dom: ^17.0.0 || ^18.0.0 || >=19.0.0-rc + rxjs: ^7.3.0 + subscriptions-transport-ws: ^0.9.0 || ^0.11.0 + peerDependenciesMeta: + graphql-ws: + optional: true + react: + optional: true + react-dom: + optional: true + subscriptions-transport-ws: + optional: true + + '@apollo/protobufjs@1.2.6': + resolution: {integrity: sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw==} + hasBin: true + + '@apollo/protobufjs@1.2.7': + resolution: {integrity: sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==} + hasBin: true + + '@apollo/usage-reporting-protobuf@4.1.1': + resolution: {integrity: sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==} + + '@apollo/utils.dropunuseddefinitions@1.1.0': + resolution: {integrity: sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg==} + engines: {node: '>=12.13.0'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + + '@apollo/utils.keyvaluecache@1.0.2': + resolution: {integrity: sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==} + + '@apollo/utils.logger@1.0.1': + resolution: {integrity: sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==} + + '@apollo/utils.printwithreducedwhitespace@1.1.0': + resolution: {integrity: sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q==} + engines: {node: '>=12.13.0'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + + '@apollo/utils.removealiases@1.0.0': + resolution: {integrity: sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A==} + engines: {node: '>=12.13.0'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + + '@apollo/utils.sortast@1.1.0': + resolution: {integrity: sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA==} + engines: {node: '>=12.13.0'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + + '@apollo/utils.stripsensitiveliterals@1.2.0': + resolution: {integrity: sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w==} + engines: {node: '>=12.13.0'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + + '@apollo/utils.usagereporting@1.0.1': + resolution: {integrity: sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ==} + engines: {node: '>=12.13.0'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + + '@apollographql/apollo-tools@0.5.4': + resolution: {integrity: sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw==} + engines: {node: '>=8', npm: '>=6'} + peerDependencies: + graphql: ^14.2.1 || ^15.0.0 || ^16.0.0 + + '@apollographql/graphql-playground-html@1.6.29': + resolution: {integrity: sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==} + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-s3@3.997.0': + resolution: {integrity: sha512-a4z12iq/bJVJXfVOOKsYMDhxZwf+n8xieCuW+zI07qtRAuMiKr2vUtHPBbKncrF+hqnsq/Wmh48bu2yziGhIbg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.973.13': + resolution: {integrity: sha512-eCFiLyBhJR7c/i8hZOETdzj2wsLFzi2L/w9/jajOgwmGqO8xrUExqkTZqdjROkwU62owqeqSuw4sIzlCv1E/ww==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/crc64-nvme@3.972.1': + resolution: {integrity: sha512-CmT9RrQol36hUdvp4dk+BRV47JBRIE+I46yAOKyb/SoMH7mKOBwk6jUpFZhF8B+LCnWnefnM6jT/WsfQ5M1kCQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.11': + resolution: {integrity: sha512-hbyoFuVm3qOAGfIPS9t7jCs8GFLFoaOs8ZmYp/chqciuHDyEGv+J365ip7YSvXSrxxUbeW9NyB1hTLt40NBMRg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.13': + resolution: {integrity: sha512-a864QxQWFkdCZ5wQF0QZNKTbqAc/DFQNeARp4gOyZZdql5RHjj4CppUSfwAzS9cpw2IPY3eeJjWqLZ1QiDB/6w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.11': + resolution: {integrity: sha512-kvPFn626ABLzxmjFMoqMRtmFKMeiUdWPhwxhmuPu233tqHnNuXzHv0MtrZlkzHd+rwlh9j0zCbQo89B54wIazQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.11': + resolution: {integrity: sha512-stdy09EpBTmsxGiXe1vB5qtXNww9wact36/uWLlSV0/vWbCOUAY2JjhPXoDVLk8n+E6r0M5HeZseLk+iTtifxg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.12': + resolution: {integrity: sha512-gMWGnHbNSKWRj+PAiuSg0EDpEwpyIgk0v9U6EuZ1C/5/BUv25Way+E+UFB7r+YYkscuBJMJ+ai8E2K0Q8dx50g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.11': + resolution: {integrity: sha512-B049fvbv41vf0Fs5bCtbzHpruBDp61sPiFDxUmkAJ/zvgSAturpj2rqzV1rj2clg4mb44Uxp9rgpcODexNFlFA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.11': + resolution: {integrity: sha512-vX9z8skN8vPtamVWmSCm4KQohub+1uMuRzIo4urZ2ZUMBAl1bqHatVD/roCb3qRfAyIGvZXCA/AWS03BQRMyCQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.11': + resolution: {integrity: sha512-VR2Ju/QBdOjnWNIYuxRml63eFDLGc6Zl8aDwLi1rzgWo3rLBgtaWhWVBAijhVXzyPdQIOqdL8hvll5ybqumjeQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.997.0': + resolution: {integrity: sha512-G5z1CBZVCRStYjgCg3Qu9LjtWAGCUa0JouPU3o8nplSIzpBE4CCJBiWZj7q1/qiIyEBxChf9apqHYJMNN1LDOQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@aws-sdk/client-s3': ^3.997.0 + + '@aws-sdk/middleware-bucket-endpoint@3.972.4': + resolution: {integrity: sha512-4W+1SPx5eWetSurqk7WNnldNr++k4UYcP2XmPnCf8yLFdUZ4NKKJA3j+zVuWmhOu7xKmEAyo9j3f+cy22CEVKg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-expect-continue@3.972.4': + resolution: {integrity: sha512-lxU2ieIWtK9nqWxA+W4ldev31tRPjkkdt+QDBWGiwUNJsNwSJFVhkuIV9cbBPxTCT0nmYyJwvJ/2TYYJLMwmMA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.972.11': + resolution: {integrity: sha512-niA/vhtS/xR4hEHIsPLEvgsccpqve+uJ4Gtizctsa21HfHmIZi5bWJD8kPcN+SfAgrlnuBG2YKFX0rRbzylg7A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.4': + resolution: {integrity: sha512-4q2Vg7/zOB10huDBLjzzTwVjBpG22X3J3ief2XrJEgTaANZrNfA3/cGbCVNAibSbu/nIYA7tDk8WCdsIzDDc4Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-location-constraint@3.972.4': + resolution: {integrity: sha512-EP1qs0JV2smcKhZpwDMuzMBx9Q5qyU/RuZ02/qh/yBA3jnZKuNhB1lsQKkicvXg7LOeoqyxXLKOP/PJOugX8yg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.4': + resolution: {integrity: sha512-xFqPvTysuZAHSkdygT+ken/5rzkR7fhOoDPejAJQslZpp0XBepmCJnDOqA57ERtCTBpu8wpjTFI1ETd4S0AXEw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.4': + resolution: {integrity: sha512-tVbRaayUZ7y2bOb02hC3oEPTqQf2A0HpPDwdMl1qTmye/q8Mq1F1WiIoFkQwG/YQFvbyErYIDMbYzIlxzzLtjQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.13': + resolution: {integrity: sha512-rGBz1n6PFxg1+5mnN1/IczesPwx0W39DZt2JPjqPiZAZ7LAqH8FS4AsawSNZqr+UFJfqtTXYpeLQnMfbMAgHhg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-ssec@3.972.4': + resolution: {integrity: sha512-jzysKNnfwqjTOeF4s1QcxYQ8WB1ZIw/KMhOAX2UGYsmpVPHZ1cV6IYRfBQnt0qnDYom1pU3b5jOG8TA9n6LAbQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.13': + resolution: {integrity: sha512-p1kVYbzBxRmhuOHoL/ANJPCedqUxnVgkEjxPoxt5pQv/yzppHM7aBWciYEE9TZY59M421D3GjLfZIZBoEFboVQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.996.1': + resolution: {integrity: sha512-XHVLFRGkuV2gh2uwBahCt65ALMb5wMpqplXEZIvFnWOCPlk60B7h7M5J9Em243K8iICDiWY6KhBEqVGfjTqlLA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.4': + resolution: {integrity: sha512-3GrJYv5eI65oCKveBZP7Q246dVP+tqeys9aKMB0dfX1glUWfppWlxIu52derqdNb9BX9lxYmeiaBcBIqOAYSgQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/s3-presigned-post@3.997.0': + resolution: {integrity: sha512-qs2kq0lAEAG+ZRUdsElMlv9W6CgtSHvUvUfORHoGUebhTS90ib/7lGc7MeTH4y+h8K54a6S5XtbSx+KgANvHgA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/s3-request-presigner@3.997.0': + resolution: {integrity: sha512-C5tV0o3OATA9lBnTYIBgKonj6gTIo1KSbmUu/FFgVN8mZvTMbJqdXqf/c83EDAFHNED+0I5zcjl9GkSGZTz7Sg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.1': + resolution: {integrity: sha512-Mj4npuEtVHFjGZHTBwhBvBzmgKHY7UsfroZWWzjpVP5YJaMTPeihsotuQLba5uQthEZyaeWs6dTu3Shr0qKFFw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.997.0': + resolution: {integrity: sha512-UdG36F7lU9aTqGFRieEyuRUJlgEJBqKeKKekC0esH21DbUSKhPR1kZBah214kYasIaWe1hLJLaqUigoTa5hZAQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.2': + resolution: {integrity: sha512-maTZwGsALtnAw4TJr/S6yERAosTwPduu0XhUV+SdbvRZtCOgSgk1ttL2R0XYzvkYSpvbtJocn77tBXq2AKglBw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-arn-parser@3.972.2': + resolution: {integrity: sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.1': + resolution: {integrity: sha512-7cJyd+M5i0IoqWkJa1KFx8KNCGIx+Ywu+lT53KpqX7ReVwz03DCKUqvZ/y65vdKwo9w9/HptSAeLDluO5MpGIg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-format-url@3.972.4': + resolution: {integrity: sha512-rPm9g4WvgTz4ko5kqseIG5Vp5LUAbWBBDalm4ogHLMc0i20ChwQWqwuTUPJSu8zXn43jIM0xO2KZaYQsFJb+ew==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.4': + resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.4': + resolution: {integrity: sha512-GHb+8XHv6hfLWKQKAKaSOm+vRvogg07s+FWtbR3+eCXXPSFn9XVmiYF4oypAxH7dGIvoxkVG/buHEnzYukyJiA==} + + '@aws-sdk/util-user-agent-node@3.972.12': + resolution: {integrity: sha512-c1n3wBK6te+Vd9qU86nF8AsYuiBsxLn0AADGWyFX7vEADr3btaAg5iPQT6GYj6rvzSOEVVisvaAatOWInlJUbQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.6': + resolution: {integrity: sha512-YrXu+UnfC8IdARa4ZkrpcyuRmA/TVgYW6Lcdtvi34NQgRjM1hTirNirN+rGb+s/kNomby8oJiIAu0KNbiZC7PA==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + engines: {node: '>=18.0.0'} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@dabh/diagnostics@2.0.8': + resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@faker-js/faker@8.4.1': + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + + '@graphql-tools/graphql-file-loader@8.1.9': + resolution: {integrity: sha512-rkLK46Q62Zxift8B6Kfw6h8SH3pCR3DPCfNeC/lpLwYReezZz+2ARuLDFZjQGjW+4lpMwiAw8CIxDyQAUgqU6A==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/import@7.1.9': + resolution: {integrity: sha512-mHzOgyfzsAgstaZPIFEtKg4GVH4FbDHeHYrSs73mAPKS5F59/FlRuUJhAoRnxbVnc3qIZ6EsWBjOjNbnPK8viA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/load@8.1.8': + resolution: {integrity: sha512-gxO662b64qZSToK3N6XUxWG5E6HOUjlg5jEnmGvD4bMtGJ0HwEe/BaVZbBQemCfLkxYjwRIBiVfOY9o0JyjZJg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/merge@8.3.1': + resolution: {integrity: sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/merge@8.4.2': + resolution: {integrity: sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/merge@9.1.7': + resolution: {integrity: sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/mock@8.7.20': + resolution: {integrity: sha512-ljcHSJWjC/ZyzpXd5cfNhPI7YljRVvabKHPzKjEs5ElxWu2cdlLGvyNYepApXDsM/OJG/2xuhGM+9GWu5gEAPQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/schema@10.0.31': + resolution: {integrity: sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/schema@8.5.1': + resolution: {integrity: sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/schema@9.0.19': + resolution: {integrity: sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@11.0.0': + resolution: {integrity: sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@8.9.0': + resolution: {integrity: sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@9.2.1': + resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-typed-document-node/core@3.2.0': + resolution: {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 + + '@grpc/grpc-js@1.14.3': + resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@ioredis/commands@1.5.0': + resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@josephg/resolvable@1.0.1': + resolution: {integrity: sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@keycloak/keycloak-admin-client@26.2.4': + resolution: {integrity: sha512-2N56jps2HEGJoslxT2LQudUNEKmVwXyfZushR77hkoQF05YQ2USFOvqeM0kxxFb/3S38ZwS4wZv0NiZKnAwhUw==} + engines: {node: '>=18'} + + '@messageformat/core@3.4.0': + resolution: {integrity: sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw==} + + '@messageformat/date-skeleton@1.1.0': + resolution: {integrity: sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A==} + + '@messageformat/number-skeleton@1.2.0': + resolution: {integrity: sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==} + + '@messageformat/parser@5.1.1': + resolution: {integrity: sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==} + + '@messageformat/runtime@3.0.2': + resolution: {integrity: sha512-dkIPDCjXcfhSHgNE1/qV6TeczQZR59Yx0xXeafVKgK3QVWoxc38ljwpksUpnzCGvN151KUbCJTDZVmahtf1YZw==} + + '@newrelic/fn-inspect@4.4.0': + resolution: {integrity: sha512-VgoXZp3zqP1167XvrA772EHDFUNuYGQh14whFq1d2sE6dC3ZL46tXI9JY0yZdAAeyCERi7mhq7CPx9BYiTOl/A==} + engines: {node: '>=16.9.1'} + + '@newrelic/native-metrics@11.1.0': + resolution: {integrity: sha512-V+FRgRgv6R8LAnVsix8d1bL5SMmBONURaT/6F//rOgycvU7PfPoVEocacpSRzPFxMI27e46iAf3W5fGosbQ8yQ==} + engines: {node: '>=18', npm: '>=6'} + + '@newrelic/security-agent@2.4.4': + resolution: {integrity: sha512-KQs76SGqTOG/pTuqRGwzy1lNAm96z8gbb5XHl9FsbOY7Kt9Ltbr46AiiovDHhNQbXFTPnHEY7FfmNJ4GK4W5dQ==} + engines: {node: '>=18', npm: '>=6.0.0'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@opentelemetry/api-logs@0.201.1': + resolution: {integrity: sha512-IxcFDP1IGMDemVFG2by/AMK+/o6EuBQ8idUq3xZ6MxgQGeumYZuX5OwR0h9HuvcUc/JPjQGfU5OHKIKYDJcXeA==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/core@2.0.1': + resolution: {integrity: sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.5.1': + resolution: {integrity: sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-metrics-otlp-http@0.201.1': + resolution: {integrity: sha512-LMRVg2yTev28L51RLLUK3gY0avMa1RVBq7IkYNtXDBxJRcd0TGGq/0rqfk7Y4UIM9NCJhDIUFHeGg8NpSgSWcw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-proto@0.201.1': + resolution: {integrity: sha512-9ie2jcaUQZdIoe6B02r0rF4Gz+JsZ9mev/2pYou1N0woOUkFM8xwO6BAlORnrFVslqF/XO5WG3q5FsTbuC5iiw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.201.1': + resolution: {integrity: sha512-FiS/mIWmZXyRxYGyXPHY+I/4+XrYVTD7Fz/zwOHkVPQsA1JTakAOP9fAi6trXMio0dIpzvQujLNiBqGM7ExrQw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.201.1': + resolution: {integrity: sha512-+q/8Yuhtu9QxCcjEAXEO8fXLjlSnrnVwfzi9jiWaMAppQp69MoagHHomQj02V2WnGjvBod5ajgkbK4IoWab50A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.0.1': + resolution: {integrity: sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/resources@2.5.1': + resolution: {integrity: sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.201.1': + resolution: {integrity: sha512-Ug8gtpssUNUnfpotB9ZhnSsPSGDu+7LngTMgKl31mmVJwLAKyl6jC8diZrMcGkSgBh0o5dbg9puvLyR25buZfw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.0.1': + resolution: {integrity: sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.5.1': + resolution: {integrity: sha512-RKMn3QKi8nE71ULUo0g/MBvq1N4icEBo7cQSKnL3URZT16/YH3nSVgWegOjwx7FRBTrjOIkMJkCUn/ZFIEfn4A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.0.1': + resolution: {integrity: sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.5.1': + resolution: {integrity: sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.39.0': + resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} + engines: {node: '>=14'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@prisma/prisma-fmt-wasm@4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085': + resolution: {integrity: sha512-zYz3rFwPB82mVlHGknAPdnSY/a308dhPOblxQLcZgZTDRtDXOE1MgxoRAys+jekwR4/bm3+rZDPs1xsFMsPZig==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@s3pweb/keycloak-admin-client-cjs@26.2.4': + resolution: {integrity: sha512-NbYC6HlAZuUIVI8L6xYDrpSzzCppqGwddPUY+L0ePYNY2rlPZKHWmnyvFYAPvQRXodgSxD/wLpio3/2acP8Ekw==} + + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@smithy/abort-controller@4.2.10': + resolution: {integrity: sha512-qocxM/X4XGATqQtUkbE9SPUB6wekBi+FyJOMbPj0AhvyvFGYEmOlz6VB22iMePCQsFmMIvFSeViDvA7mZJG47g==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.2.2': + resolution: {integrity: sha512-QzzYIlf4yg0w5TQaC9VId3B3ugSk1MI/wb7tgcHtd7CBV9gNRKZrhc2EPSxSZuDy10zUZ0lomNMgkc6/VVe8xg==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.2.1': + resolution: {integrity: sha512-y5d4xRiD6TzeP5BWlb+Ig/VFqF+t9oANNhGeMqyzU7obw7FYgTgVi50i5JqBTeKp+TABeDIeeXFZdz65RipNtA==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.9': + resolution: {integrity: sha512-ejQvXqlcU30h7liR9fXtj7PIAau1t/sFbJpgWPfiYDs7zd16jpH0IsSXKcba2jF6ChTXvIjACs27kNMc5xxE2Q==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.6': + resolution: {integrity: sha512-4xE+0L2NrsFKpEVFlFELkIHQddBvMbQ41LRIP74dGCXnY1zQ9DgksrBcRBDJT+iOzGy4VEJIeU3hkUK5mn06kg==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.10': + resolution: {integrity: sha512-3bsMLJJLTZGZqVGGeBVFfLzuRulVsGTj12BzRKODTHqUABpIr0jMN1vN3+u6r2OfyhAQ2pXaMZWX/swBK5I6PQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.10': + resolution: {integrity: sha512-A4ynrsFFfSXUHicfTcRehytppFBcY3HQxEGYiyGktPIOye3Ot7fxpiy4VR42WmtGI4Wfo6OXt/c1Ky1nUFxYYQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.10': + resolution: {integrity: sha512-0xupsu9yj9oDVuQ50YCTS9nuSYhGlrwqdaKQel9y2Fz7LU9fNErVlw9N0o4pm4qqvWEGbSTI4HKc6XJfB30MVw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.10': + resolution: {integrity: sha512-8kn6sinrduk0yaYHMJDsNuiFpXwQwibR7n/4CDUqn4UgaG+SeBHu5jHGFdU9BLFAM7Q4/gvr9RYxBHz9/jKrhA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.10': + resolution: {integrity: sha512-uUrxPGgIffnYfvIOUmBM5i+USdEBRTdh7mLPttjphgtooxQ8CtdO1p6K5+Q4BBAZvKlvtJ9jWyrWpBJYzBKsyQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.10': + resolution: {integrity: sha512-aArqzOEvcs2dK+xQVCgLbpJQGfZihw8SD4ymhkwNTtwKbnrzdhJsFDKuMQnam2kF69WzgJYOU5eJlCx+CA32bw==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.11': + resolution: {integrity: sha512-wbTRjOxdFuyEg0CpumjZO0hkUl+fetJFqxNROepuLIoijQh51aMBmzFLfoQdwRjxsuuS2jizzIUTjPWgd8pd7g==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.2.11': + resolution: {integrity: sha512-DrcAx3PM6AEbWZxsKl6CWAGnVwiz28Wp1ZhNu+Hi4uI/6C1PIZBIaPM2VoqBDAsOWbM6ZVzOEQMxFLLdmb4eBQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.10': + resolution: {integrity: sha512-1VzIOI5CcsvMDvP3iv1vG/RfLJVVVc67dCRyLSB2Hn9SWCZrDO3zvcIzj3BfEtqRW5kcMg5KAeVf1K3dR6nD3w==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.2.10': + resolution: {integrity: sha512-w78xsYrOlwXKwN5tv1GnKIRbHb1HygSpeZMP6xDxCPGf1U/xDHjCpJu64c5T35UKyEPwa0bPeIcvU69VY3khUA==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.10': + resolution: {integrity: sha512-vy9KPNSFUU0ajFYk0sDZIYiUlAWGEAhRfehIr5ZkdFrRFTAuXEPUd41USuqHU6vvLX4r6Q9X7MKBco5+Il0Org==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.1': + resolution: {integrity: sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.2.10': + resolution: {integrity: sha512-Op+Dh6dPLWTjWITChFayDllIaCXRofOed8ecpggTC5fkh8yXes0vAEX7gRUfjGK+TlyxoCAA05gHbZW/zB9JwQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.10': + resolution: {integrity: sha512-TQZ9kX5c6XbjhaEBpvhSvMEZ0klBs1CFtOdPFwATZSbC9UeQfKHPLPN9Y+I6wZGMOavlYTOlHEPDrt42PMSH9w==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.20': + resolution: {integrity: sha512-9W6Np4ceBP3XCYAGLoMCmn8t2RRVzuD1ndWPLBbv7H9CrwM9Bprf6Up6BM9ZA/3alodg0b7Kf6ftBK9R1N04vw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.37': + resolution: {integrity: sha512-/1psZZllBBSQ7+qo5+hhLz7AEPGLx3Z0+e3ramMBEuPK2PfvLK4SrncDB9VegX5mBn+oP/UTDrM6IHrFjvX1ZA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.11': + resolution: {integrity: sha512-STQdONGPwbbC7cusL60s7vOa6He6A9w2jWhoapL0mgVjmR19pr26slV+yoSP76SIssMTX/95e5nOZ6UQv6jolg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.10': + resolution: {integrity: sha512-pmts/WovNcE/tlyHa8z/groPeOtqtEpp61q3W0nW1nDJuMq/x+hWa/OVQBtgU0tBqupeXq0VBOLA4UZwE8I0YA==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.10': + resolution: {integrity: sha512-UALRbJtVX34AdP2VECKVlnNgidLHA2A7YgcJzwSBg1hzmnO/bZBHl/LDQQyYifzUwp1UOODnl9JJ3KNawpUJ9w==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.4.12': + resolution: {integrity: sha512-zo1+WKJkR9x7ZtMeMDAAsq2PufwiLDmkhcjpWPRRkmeIuOm6nq1qjFICSZbnjBvD09ei8KMo26BWxsu2BUU+5w==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.10': + resolution: {integrity: sha512-5jm60P0CU7tom0eNrZ7YrkgBaoLFXzmqB0wVS+4uK8PPGmosSrLNf6rRd50UBvukztawZ7zyA8TxlrKpF5z9jw==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.10': + resolution: {integrity: sha512-2NzVWpYY0tRdfeCJLsgrR89KE3NTWT2wGulhNUxYlRmtRmPwLQwKzhrfVaiNlA9ZpJvbW7cjTVChYKgnkqXj1A==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.10': + resolution: {integrity: sha512-HeN7kEvuzO2DmAzLukE9UryiUvejD3tMp9a1D1NJETerIfKobBUCLfviP6QEk500166eD2IATaXM59qgUI+YDA==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.10': + resolution: {integrity: sha512-4Mh18J26+ao1oX5wXJfWlTT+Q1OpDR8ssiC9PDOuEgVBGloqg18Fw7h5Ct8DyT9NBYwJgtJ2nLjKKFU6RP1G1Q==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.10': + resolution: {integrity: sha512-0R/+/Il5y8nB/By90o8hy/bWVYptbIfvoTYad0igYQO5RefhNCDmNzqxaMx7K1t/QWo0d6UynqpqN5cCQt1MCg==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.5': + resolution: {integrity: sha512-pHgASxl50rrtOztgQCPmOXFjRW+mCd7ALr/3uXNzRrRoGV5G2+78GOsQ3HlQuBVHCh9o6xqMNvlIKZjWn4Euug==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.10': + resolution: {integrity: sha512-Wab3wW8468WqTKIxI+aZe3JYO52/RYT/8sDOdzkUhjnLakLe9qoQqIcfih/qxcF4qWEFoWBszY0mj5uxffaVXA==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.0': + resolution: {integrity: sha512-R8bQ9K3lCcXyZmBnQqUZJF4ChZmtWT5NLi6x5kgWx5D+/j0KorXcA0YcFg/X5TOgnTCy1tbKc6z2g2y4amFupQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.13.0': + resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.10': + resolution: {integrity: sha512-uypjF7fCDsRk26u3qHmFI/ePL7bxxB9vKkE+2WKEciHhz+4QtbzWiHRVNRJwU3cKhrYDYQE3b0MRFtqfLYdA4A==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.1': + resolution: {integrity: sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.1': + resolution: {integrity: sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.2': + resolution: {integrity: sha512-4rHqBvxtJEBvsZcFQSPQqXP2b/yy/YlB66KlcEgcH2WNoOKCKB03DSLzXmOsXjbl8dJ4OEYTn31knhdznwk7zw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.1': + resolution: {integrity: sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.1': + resolution: {integrity: sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.36': + resolution: {integrity: sha512-R0smq7EHQXRVMxkAxtH5akJ/FvgAmNF6bUy/GwY/N20T4GrwjT633NFm0VuRpC+8Bbv8R9A0DoJ9OiZL/M3xew==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.39': + resolution: {integrity: sha512-otWuoDm35btJV1L8MyHrPl462B07QCdMTktKc7/yM+Psv6KbED/ziXiHnmr7yPHUjfIwE9S8Max0LO24Mo3ZVg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.1': + resolution: {integrity: sha512-xyctc4klmjmieQiF9I1wssBWleRV0RhJ2DpO8+8yzi2LO1Z+4IWOZNGZGNj4+hq9kdo+nyfrRLmQTzc16Op2Vg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.1': + resolution: {integrity: sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.10': + resolution: {integrity: sha512-LxaQIWLp4y0r72eA8mwPNQ9va4h5KeLM0I3M/HV9klmFaY2kN766wf5vsTzmaOpNNb7GgXAd9a25P3h8T49PSA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.10': + resolution: {integrity: sha512-HrBzistfpyE5uqTwiyLsFHscgnwB0kgv8vySp7q5kZ0Eltn/tjosaSGGDj/jJ9ys7pWzIP/icE2d+7vMKXLv7A==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.15': + resolution: {integrity: sha512-OlOKnaqnkU9X+6wEkd7mN+WB7orPbCVDauXOj22Q7VtiTkvy7ZdSsOg4QiNAZMgI4OkvNf+/VLUC3VXkxuWJZw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.1': + resolution: {integrity: sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.1': + resolution: {integrity: sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.10': + resolution: {integrity: sha512-4eTWph/Lkg1wZEDAyObwme0kmhEb7J/JjibY2znJdrYRgKbKqB7YoEhhJVJ4R1g/SYih4zuwX7LpJaM8RsnTVg==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.1': + resolution: {integrity: sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw==} + engines: {node: '>=18.0.0'} + + '@so-ric/colorspace@1.1.6': + resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + + '@supercharge/request-ip@1.2.0': + resolution: {integrity: sha512-wlt6JW69MHqLY2M6Sm/jVyCojNRKq2CBvwH0Hbx24SFhDQQGkgEjeKxVutDxHSyrWixFaOSLXC27euzxijhyMQ==} + + '@testim/chrome-version@1.1.4': + resolution: {integrity: sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==} + + '@theguild/federation-composition@0.21.3': + resolution: {integrity: sha512-+LlHTa4UbRpZBog3ggAxjYIFvdfH3UMvvBUptur19TMWkqU4+n3GmN+mDjejU+dyBXIG27c25RsiQP1HyvM99g==} + engines: {node: '>=18'} + peerDependencies: + graphql: ^16.0.0 + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@types/accepts@1.3.7': + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + + '@types/amqp-connection-manager@2.0.12': + resolution: {integrity: sha512-2GX1jG6ECpEXQF0X68gTTZc8MQ8GA0dM2mAd1irTpWlKzGKlGzCBtb1YnqLHozNNsoLtGI6UXSp0q06jU1LA6g==} + + '@types/amqplib@0.10.8': + resolution: {integrity: sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/body-parser@1.19.2': + resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/busboy@1.5.4': + resolution: {integrity: sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/cors@2.8.12': + resolution: {integrity: sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==} + + '@types/express-serve-static-core@4.17.31': + resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} + + '@types/express-serve-static-core@4.19.8': + resolution: {integrity: sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==} + + '@types/express@4.17.14': + resolution: {integrity: sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==} + + '@types/express@4.17.25': + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/jsonwebtoken@9.0.10': + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + + '@types/long@4.0.2': + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + + '@types/luxon@3.4.2': + resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node-cache@4.2.5': + resolution: {integrity: sha512-faK2Owokboz53g8ooq2dw3iDJ6/HMTCIa2RvMte5WMTiABy+wA558K+iuyRtlR67Un5q9gEKysSDtqZYbSa0Pg==} + deprecated: This is a stub types definition. node-cache provides its own type definitions, so you do not need this installed. + + '@types/node@10.17.60': + resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} + + '@types/node@25.3.0': + resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==} + + '@types/object-path@0.11.4': + resolution: {integrity: sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/ramda@0.31.1': + resolution: {integrity: sha512-Vt6sFXnuRpzaEj+yeutA0q3bcAsK7wdPuASIzR9LXqL4gJPyFw8im9qchlbp4ltuf3kDEIRmPJTD/Fkg60dn7g==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/redis@2.8.32': + resolution: {integrity: sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==} + + '@types/send@0.17.6': + resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} + + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-static@1.15.10': + resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + + '@types/sha1@1.1.5': + resolution: {integrity: sha512-eE1PzjW7u2VfxI+bTsvjzjBfpwqvxSpgfUmnRNVY+PJU1NBsdGZlaO/qnVnPKHzzpgIl9YyBIxvrgBvt1mzt2A==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@8.56.1': + resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.56.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@6.21.0': + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@8.56.1': + resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.56.1': + resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@6.21.0': + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/scope-manager@8.56.1': + resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.56.1': + resolution: {integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@6.21.0': + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/types@8.56.1': + resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@6.21.0': + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.56.1': + resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@6.21.0': + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/visitor-keys@8.56.1': + resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@tyriar/fibonacci-heap@2.0.9': + resolution: {integrity: sha512-bYuSNomfn4hu2tPiDN+JZtnzCpSpbJ/PNeulmocDy3xN2X5OkJL65zo6rPZp65cPPhLF9vfT/dgE+RtFRCSxOA==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@whatwg-node/promise-helpers@1.3.2': + resolution: {integrity: sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==} + engines: {node: '>=16.0.0'} + + '@wry/caches@1.0.1': + resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==} + engines: {node: '>=8'} + + '@wry/context@0.7.4': + resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==} + engines: {node: '>=8'} + + '@wry/equality@0.5.7': + resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} + engines: {node: '>=8'} + + '@wry/trie@0.5.0': + resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==} + engines: {node: '>=8'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agentkeepalive@3.5.3: + resolution: {integrity: sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==} + engines: {node: '>= 4.0.0'} + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + amqp-connection-manager@4.1.15: + resolution: {integrity: sha512-YGi8MuO2K4u0qC4b1qAKVIK8CdBJF+PpEZpHKxmrL3x61pU1sAb2SHzQl7ypoqr6oECIfVqtG9DGdOZxaUq1Wg==} + engines: {node: '>=10.0.0', npm: '>5.0.0'} + peerDependencies: + amqplib: '*' + + amqplib@0.10.9: + resolution: {integrity: sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==} + engines: {node: '>=10'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + apollo-datasource@3.3.2: + resolution: {integrity: sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg==} + engines: {node: '>=12.0'} + deprecated: The `apollo-datasource` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + + apollo-reporting-protobuf@3.4.0: + resolution: {integrity: sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog==} + deprecated: The `apollo-reporting-protobuf` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/usage-reporting-protobuf` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + + apollo-server-core@3.13.0: + resolution: {integrity: sha512-v/g6DR6KuHn9DYSdtQijz8dLOkP78I5JSVJzPkARhDbhpH74QNwrQ2PP2URAPPEDJ2EeZNQDX8PvbYkAKqg+kg==} + engines: {node: '>=12.0'} + deprecated: The `apollo-server-core` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + peerDependencies: + graphql: ^15.3.0 || ^16.0.0 + + apollo-server-env@4.2.1: + resolution: {integrity: sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g==} + engines: {node: '>=12.0'} + deprecated: The `apollo-server-env` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/utils.fetcher` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + + apollo-server-errors@3.3.1: + resolution: {integrity: sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA==} + engines: {node: '>=12.0'} + deprecated: The `apollo-server-errors` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + peerDependencies: + graphql: ^15.3.0 || ^16.0.0 + + apollo-server-express@3.13.0: + resolution: {integrity: sha512-iSxICNbDUyebOuM8EKb3xOrpIwOQgKxGbR2diSr4HP3IW8T3njKFOoMce50vr+moOCe1ev8BnLcw9SNbuUtf7g==} + engines: {node: '>=12.0'} + deprecated: The `apollo-server-express` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + peerDependencies: + express: ^4.17.1 + graphql: ^15.3.0 || ^16.0.0 + + apollo-server-plugin-base@3.7.2: + resolution: {integrity: sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw==} + engines: {node: '>=12.0'} + deprecated: The `apollo-server-plugin-base` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + peerDependencies: + graphql: ^15.3.0 || ^16.0.0 + + apollo-server-types@3.8.0: + resolution: {integrity: sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A==} + engines: {node: '>=12.0'} + deprecated: The `apollo-server-types` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. + peerDependencies: + graphql: ^15.3.0 || ^16.0.0 + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + + asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + auto-parse@1.8.0: + resolution: {integrity: sha512-Uri4uC+K5cSi5hjM4snFrqPrjqUpwxeSW5EMTPvN7Ju3PlDzmXXDr5tjdzxPvvwgT3J7bmMDJ3Rm625nbrc72A==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + aws-sdk@2.1693.0: + resolution: {integrity: sha512-cJmb8xEnVLT+R6fBS5sn/EFJiX7tUnDaPtOPZ1vFbOJtd0fnZn/Ky2XGgsvvoeliWeH7mL3TWSX5zXXGSQV6gQ==} + engines: {node: '>= 10.0.0'} + deprecated: The AWS SDK for JavaScript (v2) has reached end-of-support, and no longer receives updates. Please migrate your code to use AWS SDK for JavaScript (v3). More info https://a.co/cUPnyil + + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + babel-runtime@6.26.0: + resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} + hasBin: true + + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + + basic-ftp@5.2.0: + resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} + engines: {node: '>=10.0.0'} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bn.js@4.12.3: + resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} + + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + boolify@1.0.1: + resolution: {integrity: sha512-ma2q0Tc760dW54CdOyJjhrg/a54317o1zYADQJFgperNGKIKgAUGIcKnuMiff8z57+yGlrGNEt4lPgZfCgTJgA==} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + brace-expansion@5.0.3: + resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-more-ints@1.0.0: + resolution: {integrity: sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==} + + buffer@4.9.2: + resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} + + buffer@5.6.0: + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-keys@4.2.0: + resolution: {integrity: sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==} + engines: {node: '>=4'} + + camelcase-keys@9.1.3: + resolution: {integrity: sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==} + engines: {node: '>=16'} + + camelcase@4.1.0: + resolution: {integrity: sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==} + engines: {node: '>=4'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + camelize-ts@3.0.0: + resolution: {integrity: sha512-cgRwKKavoDKLTjO4FQTs3dRBePZp/2Y9Xpud0FhuCOTE86M2cniKN4CCXgRnsyXNMmQMifVHcv6SPaMtTx6ofQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + caniuse-lite@1.0.30001774: + resolution: {integrity: sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==} + + chalk@1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + charenc@0.0.2: + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + + check-disk-space@3.4.0: + resolution: {integrity: sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==} + engines: {node: '>=16'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chromedriver@146.0.0: + resolution: {integrity: sha512-fDAbuEy+Dn9F/h8fphiQIUEyUDOTGlfjZHfI9dJZz75+ui/LIHqWzStQt87vpwA9oV3ut4C2W3flfvbn3KELFQ==} + engines: {node: '>=20'} + hasBin: true + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-convert@3.1.3: + resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==} + engines: {node: '>=14.6'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-name@2.1.0: + resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} + engines: {node: '>=12.20'} + + color-string@2.1.4: + resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} + engines: {node: '>=18'} + + color@5.0.3: + resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} + engines: {node: '>=18'} + + colorette@2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + constant-case@3.0.4: + resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + core-js@2.6.12: + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} + deprecated: core-js@<3.23.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. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + + core-js@3.48.0: + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + cron@3.5.0: + resolution: {integrity: sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==} + + cross-inspect@1.0.1: + resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} + engines: {node: '>=16.0.0'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + + cssfilter@0.0.10: + resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + date-format@4.0.14: + resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} + engines: {node: '>=4.0'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.1: + resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dedent@1.7.1: + resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + denque@1.5.1: + resolution: {integrity: sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==} + engines: {node: '>=0.10'} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dotenv-extended@2.9.0: + resolution: {integrity: sha512-MKc4WCqZj6Abx4rpDbQ9LsuBJldRLxLgFkY5qE+4JM7hXVYT/v8zyWGgnBeDjSOGzEecWOFPlosNpxfB9YnsCw==} + engines: {node: '>=6'} + hasBin: true + + dotenv@4.0.0: + resolution: {integrity: sha512-XcaMACOr3JMVcEv0Y/iUM2XaOsATRZ3U1In41/1jjK6vJZ2PZbQ1bzCG8uvaByfaBpl9gqc9QWJovpUGBXLLYQ==} + engines: {node: '>=4.6.0'} + + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + elasticsearch@15.5.0: + resolution: {integrity: sha512-ZGKKaDkOFAap61ObBNkAxhYXCcAbRfkI4NVoSeLGnTD6/cItvY2j9LII/VV8/zclGe1x5m6DsVp47E4ze4aAeQ==} + engines: {node: '>=0.8'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + electron-to-chromium@1.5.302: + resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-airbnb-base@15.0.0: + resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.2 + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + + events@1.1.1: + resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} + engines: {node: '>=0.4.x'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-xml-parser@5.3.6: + resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==} + hasBin: true + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + find-package-json@1.2.0: + resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + + fs-capacitor@8.0.0: + resolution: {integrity: sha512-+Lk6iSKajdGw+7XYxUkwIzreJ2G1JFlYOdnKJv5PzwFLVsoJYBpCuS7WPIUSNT1IbQaEWT1nhYU63Ud03DyzLA==} + engines: {node: ^14.17.0 || >=16.0.0} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.name@1.0.14: + resolution: {integrity: sha512-s99L814NRuLxwF2sJMIcLhkQhueGXb3oKyvorzrUKKwlVB0SBbWrgZt4+EwKAo3ujCXnT7vshmCvXgZA09kCMw==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} + engines: {node: '>= 14'} + + getopts@2.3.0: + resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} + + git-up@7.0.0: + resolution: {integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==} + + git-url-parse@13.1.1: + resolution: {integrity: sha512-PCFJyeSSdtnbfhSNRw9Wk96dDCNx+sogTe4YNXeXSJxt7xz5hvXekuRn9JX7m+Mf4OscCu8h+mtAl3+h5Fo8lQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.5.0: + resolution: {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 + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + 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 + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + graphql-iso-date@3.6.1: + resolution: {integrity: sha512-AwFGIuYMJQXOEAgRlJlFL4H1ncFM8n8XmoVDTNypNOZyQ8LFDG2ppMFlsS862BSTCDcSUfHp8PD3/uJhv7t59Q==} + peerDependencies: + graphql: ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 + + graphql-redis-subscriptions@2.7.0: + resolution: {integrity: sha512-IK4uCKx1UNhkcnG9lIqFWz9PpltSbuM8RygwGoB/e1HZMuKpAGeqqfHFeLKkQjjubvk4tAdUdx48AUkTAXJ17Q==} + peerDependencies: + graphql-subscriptions: ^1.0.0 || ^2.0.0 || ^3.0.0 + + graphql-sse@2.6.0: + resolution: {integrity: sha512-BXT5Rjv9UFunjQsmN9WWEIq+TFNhgYibgwo1xkXLxzguQVyOd6paJ4v5DlL9K5QplS0w74bhF+aUiqaGXZBaug==} + engines: {node: '>=12'} + peerDependencies: + graphql: '>=0.11 <=16' + + graphql-subscriptions@1.2.1: + resolution: {integrity: sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g==} + peerDependencies: + graphql: ^0.10.5 || ^0.11.3 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 + + graphql-tag@2.12.6: + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + + graphql-tools@9.0.26: + resolution: {integrity: sha512-vd5w0PWgI1dE0/C3UB6VrRtyML3TpomyazYGUYZxEOs7nSkRfvCKcDbP3TEu+yfWIRN3AbXPmW19tUCC53yXig==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + graphql-type-json@0.3.2: + resolution: {integrity: sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==} + peerDependencies: + graphql: '>=0.8.0' + + graphql-upload@17.0.0: + resolution: {integrity: sha512-AI42S1UR1mdqg+LQ7KqGbrgcf4l9gpPu/R0drM4vSA5C94NfIjYyCeCdpktEledvZoAL8JURLLeB53++WACo1w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=22.0.0} + peerDependencies: + '@types/express': 4.0.29 - 5 + '@types/koa': ^2.11.4 + graphql: ^16.3.0 + peerDependenciesMeta: + '@types/express': + optional: true + '@types/koa': + optional: true + + graphql-ws@5.16.0: + resolution: {integrity: sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==} + engines: {node: '>=10'} + peerDependencies: + graphql: '>=0.11 <=16' + + graphql@16.13.0: + resolution: {integrity: sha512-uSisMYERbaB9bkA9M4/4dnqyktaEkf1kMHNKq/7DHyxVeWqHQ2mBmVqm5u6/FVHwF3iCNalKcg82Zfl+tffWoA==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-ansi@2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + ieee754@1.1.13: + resolution: {integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-in-the-middle@1.15.0: + resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + interpret@2.2.0: + resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} + engines: {node: '>= 0.10'} + + ioredis@5.9.3: + resolution: {integrity: sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==} + engines: {node: '>=12.22.0'} + + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + + ip-regex@4.3.0: + resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} + engines: {node: '>=8'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-invalid-path@1.0.2: + resolution: {integrity: sha512-6KLcFrPCEP3AFXMfnWrIFkZpYNBVzZAoBJJDEZKtI3LXkaDjM3uFMJQjxiizUuZTZ9Oh9FNv/soXbx5TcpaDmA==} + engines: {node: '>=6.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-ssh@1.4.1: + resolution: {integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is2@2.0.9: + resolution: {integrity: sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==} + engines: {node: '>=v0.10.0'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + iterall@1.3.0: + resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jmespath@0.16.0: + resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} + engines: {node: '>= 0.6.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonschema@1.5.0: + resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==} + + jsonwebtoken@8.5.1: + resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} + engines: {node: '>=4', npm: '>=1.4.28'} + + jwa@1.4.2: + resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} + + jwk-to-pem@2.0.7: + resolution: {integrity: sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ==} + + jws@3.2.3: + resolution: {integrity: sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==} + + keycloak-connect@26.1.1: + resolution: {integrity: sha512-2wvNJXldB9Em+mp6liJ+AnftcJovFEvNhUgv3hblNDmVihBoBqn4zFlwLIN41lo0H8CicB2T86xZ5U2MiQ9FFA==} + engines: {node: '>=14'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + knex@3.1.0: + resolution: {integrity: sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==} + engines: {node: '>=16'} + hasBin: true + peerDependencies: + better-sqlite3: '*' + mysql: '*' + mysql2: '*' + pg: '*' + pg-native: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + better-sqlite3: + optional: true + mysql: + optional: true + mysql2: + optional: true + pg: + optional: true + pg-native: + optional: true + sqlite3: + optional: true + tedious: + optional: true + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + + log4js@6.9.1: + resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} + engines: {node: '>=8.0'} + + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + + loglevel-colored-level-prefix@1.0.0: + resolution: {integrity: sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==} + + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + + lokka@1.7.0: + resolution: {integrity: sha512-4/XqX7LfkldHSDsHbl7alMdB0AINdsUTnqIcN6CwDa4srFpRR3iatx+6ULK2KDgS1JPKPDOTVuyhhoXZ+EgH7Q==} + + long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lru-cache@7.13.1: + resolution: {integrity: sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==} + engines: {node: '>=12'} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru.min@1.1.4: + resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + + luxon@3.5.0: + resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} + engines: {node: '>=12'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + make-plural@7.5.0: + resolution: {integrity: sha512-0booA+aVYyVFoR67JBHdfVk0U08HmrBH2FrtmBqBa+NldlqXv/G2Z9VQuQq6Wgp2jDWdybEWGfBkk1cq5264WA==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + map-obj@2.0.0: + resolution: {integrity: sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==} + engines: {node: '>=4'} + + map-obj@5.0.0: + resolution: {integrity: sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + mariadb@3.5.1: + resolution: {integrity: sha512-ObKf/e7jU8pmwa5wTs1UwhLMrQYRBDwXPndej2Dv/rvbqsokJYlymubsNtpakVfjI+hd0bG3U6tiuVunDYoLtA==} + engines: {node: '>= 18'} + peerDependencies: + '@types/geojson': '>=7946.0.0' + '@types/node': '>=18' + peerDependenciesMeta: + '@types/geojson': + optional: true + '@types/node': + optional: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.4: + resolution: {integrity: sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.7: + resolution: {integrity: sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + + morgan@1.10.1: + resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==} + engines: {node: '>= 0.8.0'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mysql2@3.18.0: + resolution: {integrity: sha512-3rupyOFks7Vq0jcjBpmg1gtgfGuCcmgrRJPEfpGzzrB/ydutupbjKkoDJGsGkrJRU6j44o2tb0McduL03/v/dQ==} + engines: {node: '>= 8.0'} + peerDependencies: + '@types/node': '>= 8' + + named-placeholders@1.1.6: + resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} + engines: {node: '>=8.0.0'} + + nan@2.25.0: + resolution: {integrity: sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + newrelic@12.25.1: + resolution: {integrity: sha512-CcVkLRiVRhfiVQeGJ3bjpMfM6yhaK9gxG+jQCR5DhcG94hTgLCCND1xwdlVqZwPG5LMlPddkDXWmXY7jd6P5DQ==} + engines: {node: '>=18', npm: '>=6.0.0'} + hasBin: true + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abi@3.87.0: + resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==} + engines: {node: '>=10'} + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-cache@5.1.2: + resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==} + engines: {node: '>= 8.0.0'} + + node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + noop6@1.0.10: + resolution: {integrity: sha512-WZvuCILZFZHK+WuqCQwxLBGllkBK1ct8s8Mu9FMDbEsBE6/bqNxyFGbX7Xky+6bYFL8X2Ou4Cis4CJyrwXLvQA==} + + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@3.1.0: + resolution: {integrity: sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==} + engines: {node: '>=8'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object-path@0.11.8: + resolution: {integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==} + engines: {node: '>= 10.12.0'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + optimism@0.18.1: + resolution: {integrity: sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-path@7.1.0: + resolution: {integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==} + + parse-url@8.1.0: + resolution: {integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + pg-connection-string@2.6.2: + resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + prebuildify@6.0.1: + resolution: {integrity: sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw==} + hasBin: true + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-eslint-cli@8.0.1: + resolution: {integrity: sha512-jru4JUDHzWEtM/SOxqagU7hQTVP8BVrxO2J0qNauWZuPRld6Ea2eyNaEzIGx6I+yjmOLCsjNM+vU1AJgaW1ZSQ==} + engines: {node: '>=16.10.0'} + hasBin: true + peerDependencies: + prettier-eslint: '*' + peerDependenciesMeta: + prettier-eslint: + optional: true + + prettier-eslint@16.4.2: + resolution: {integrity: sha512-vtJAQEkaN8fW5QKl08t7A5KCjlZuDUNeIlr9hgolMS5s3+uzbfRHDwaRnzrdqnY2YpHDmeDS/8zY0MKQHXJtaA==} + engines: {node: '>=16.10.0'} + peerDependencies: + prettier-plugin-svelte: ^3.0.0 + svelte-eslint-parser: '*' + peerDependenciesMeta: + prettier-plugin-svelte: + optional: true + svelte-eslint-parser: + optional: true + + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + promise-breaker@6.0.0: + resolution: {integrity: sha512-BthzO9yTPswGf7etOBiHCVuugs2N01/Q/94dIPls48z2zCmrnDptUUZzfIb+41xq0MnYZ/BzmOd6ikDR4ibNZA==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + protocols@2.0.2: + resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + proxy-from-env@2.0.0: + resolution: {integrity: sha512-h2lD3OfRraP3R51rNFKIE8nX+qoLr1mE74X91YhVxtDbt+OD6ntoNZv56+JgI4RCdtwQ5eexsOk1KdOQDfvPCQ==} + + ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@1.3.2: + resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + qs@6.14.2: + resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} + engines: {node: '>=0.6'} + + querystring@0.2.0: + resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} + engines: {node: '>=0.4.x'} + deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@1.1.0: + resolution: {integrity: sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==} + engines: {node: '>=4'} + + quick-lru@6.1.2: + resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==} + engines: {node: '>=12'} + + ramda@0.32.0: + resolution: {integrity: sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + redis-commands@1.7.0: + resolution: {integrity: sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==} + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + + redis@3.1.2: + resolution: {integrity: sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==} + engines: {node: '>=10'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerator-runtime@0.11.1: + resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + request-ip@3.3.0: + resolution: {integrity: sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@7.5.2: + resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} + engines: {node: '>=8.6.0'} + + require-relative@0.8.7: + resolution: {integrity: sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + ringbufferjs@2.0.0: + resolution: {integrity: sha512-GCOqTzUsTHF7nrqcgtNGAFotXztLgiePpIDpyWZ7R5I02tmfJWV+/yuJc//Hlsd8G+WzI1t/dc2y/w2imDZdog==} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-identifier@0.4.2: + resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.2.1: + resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + + sha1@1.1.1: + resolution: {integrity: sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + snakecase-keys@1.2.0: + resolution: {integrity: sha512-G5Faa3wQevGXcD5e4JKfmgofO+Fu4Jg4/nLyeZqWmBqVV0/3ORgervt3EjBi6PEFKhztPQWegZspteWnycx5dg==} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sql-escaper@1.3.3: + resolution: {integrity: sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==} + engines: {bun: '>=1.0.0', deno: '>=2.0.0', node: '>=12.0.0'} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + + streamroller@3.1.5: + resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} + engines: {node: '>=8.0'} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.fromcodepoint@0.2.1: + resolution: {integrity: sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strnum@2.1.2: + resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tarn@3.0.2: + resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} + engines: {node: '>=8.0.0'} + + tcp-port-used@1.0.2: + resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tildify@2.0.0: + resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} + engines: {node: '>=8'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + + to-no-case@0.1.1: + resolution: {integrity: sha512-XNChsa36ssNQibYbv/kBSLb0jEfPuePdnbX1tBpHypUbtjBSR8ihaDRRYPZLMstjSpPM+onSgxeDjg+lgfIzZQ==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + to-snake-case@0.1.2: + resolution: {integrity: sha512-0h2qEd1GZjWTbUqKRVo7D6ZdhM5H/2hUlA4g1+kQE4C27709WEVmbtAE+PQbYQ8iLgcyoHWMIN7fx6t8kg4Zkg==} + + to-space-case@0.1.2: + resolution: {integrity: sha512-DtF9QZwx8W6WMhrCuvUZJYX9sT74/VOdVxi68EkEu3gt0SUQHhJG+UYbQI0559XcchsX7gziPlWuV4dEGC59dA==} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-jest@29.4.6: + resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: '*' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + + tsc-watch@6.3.1: + resolution: {integrity: sha512-x1hNJ/m1Cln2OvNJJLPsnWI1pObm+Jq9jF6kyz0flEy/ym7Y9TvU9edg6lXLuGVkhDClwBWi8aWAFE0qgzEkzw==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + types-ramda@0.31.0: + resolution: {integrity: sha512-vaoC35CRC3xvL8Z6HkshDbi6KWM1ezK0LHN0YyxXWUn9HKzBNg/T3xSGlJZjCYspnOD3jE7bcizsp0bUXZDxnQ==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + typpy@2.3.11: + resolution: {integrity: sha512-Jh/fykZSaxeKO0ceMAs6agki9T5TNA9kiIR6fzKbvafKpIw8UlNlHhzuqKyi5lfJJ5VojJOx9tooIbyy7vHV/g==} + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + unescape-js@1.1.4: + resolution: {integrity: sha512-42SD8NOQEhdYntEiUQdYq/1V/YHwr1HLwlHuTJB5InVVdOSbgI6xu8jK5q65yIzuFCfczzyDF/7hbGzVbyCw0g==} + + unescape@1.0.1: + resolution: {integrity: sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==} + engines: {node: '>=0.10.0'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + unixify@1.0.0: + resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} + engines: {node: '>=0.10.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + upper-case@2.0.2: + resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-join@5.0.0: + resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + url-template@3.1.1: + resolution: {integrity: sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + url@0.10.3: + resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid4@1.1.4: + resolution: {integrity: sha512-Gr1q2k40LpF8CokcnQFjPDsdslzJbTCTBG5xQIEflUov431gFkY5KduiGIeKYAamkQnNn4IfdHJbLnl9Bib8TQ==} + + uuid@2.0.3: + resolution: {integrity: sha512-FULf7fayPdpASncVy4DLh3xydlXEJJpvIELjYjNeQWYUZ9pclcpvCZSr2gkmN2FrrGcI7G/cJsIEwk5/8vfXpg==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + + uuid@8.0.0: + resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validator@13.15.26: + resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} + engines: {node: '>= 0.10'} + + value-or-promise@1.0.11: + resolution: {integrity: sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==} + engines: {node: '>=12'} + + value-or-promise@1.0.12: + resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==} + engines: {node: '>=12'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.19.0: + resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + xss@1.0.15: + resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} + engines: {node: '>= 0.10.0'} + hasBin: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@apollo/client@4.0.13(graphql-ws@5.16.0(graphql@16.13.0))(graphql@16.13.0)(rxjs@7.8.2)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) + '@wry/caches': 1.0.1 + '@wry/equality': 0.5.7 + '@wry/trie': 0.5.0 + graphql: 16.13.0 + graphql-tag: 2.12.6(graphql@16.13.0) + optimism: 0.18.1 + rxjs: 7.8.2 + tslib: 2.8.1 + optionalDependencies: + graphql-ws: 5.16.0(graphql@16.13.0) + optional: true + + '@apollo/protobufjs@1.2.6': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.2 + '@types/node': 10.17.60 + long: 4.0.0 + + '@apollo/protobufjs@1.2.7': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.2 + long: 4.0.0 + + '@apollo/usage-reporting-protobuf@4.1.1': + dependencies: + '@apollo/protobufjs': 1.2.7 + + '@apollo/utils.dropunuseddefinitions@1.1.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + + '@apollo/utils.keyvaluecache@1.0.2': + dependencies: + '@apollo/utils.logger': 1.0.1 + lru-cache: 7.13.1 + + '@apollo/utils.logger@1.0.1': {} + + '@apollo/utils.printwithreducedwhitespace@1.1.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + + '@apollo/utils.removealiases@1.0.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + + '@apollo/utils.sortast@1.1.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + lodash.sortby: 4.7.0 + + '@apollo/utils.stripsensitiveliterals@1.2.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + + '@apollo/utils.usagereporting@1.0.1(graphql@16.13.0)': + dependencies: + '@apollo/usage-reporting-protobuf': 4.1.1 + '@apollo/utils.dropunuseddefinitions': 1.1.0(graphql@16.13.0) + '@apollo/utils.printwithreducedwhitespace': 1.1.0(graphql@16.13.0) + '@apollo/utils.removealiases': 1.0.0(graphql@16.13.0) + '@apollo/utils.sortast': 1.1.0(graphql@16.13.0) + '@apollo/utils.stripsensitiveliterals': 1.2.0(graphql@16.13.0) + graphql: 16.13.0 + + '@apollographql/apollo-tools@0.5.4(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + + '@apollographql/graphql-playground-html@1.6.29': + dependencies: + xss: 1.0.15 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.2 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.2 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-locate-window': 3.965.4 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-locate-window': 3.965.4 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.2 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.997.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.13 + '@aws-sdk/credential-provider-node': 3.972.12 + '@aws-sdk/middleware-bucket-endpoint': 3.972.4 + '@aws-sdk/middleware-expect-continue': 3.972.4 + '@aws-sdk/middleware-flexible-checksums': 3.972.11 + '@aws-sdk/middleware-host-header': 3.972.4 + '@aws-sdk/middleware-location-constraint': 3.972.4 + '@aws-sdk/middleware-logger': 3.972.4 + '@aws-sdk/middleware-recursion-detection': 3.972.4 + '@aws-sdk/middleware-sdk-s3': 3.972.13 + '@aws-sdk/middleware-ssec': 3.972.4 + '@aws-sdk/middleware-user-agent': 3.972.13 + '@aws-sdk/region-config-resolver': 3.972.4 + '@aws-sdk/signature-v4-multi-region': 3.996.1 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-endpoints': 3.996.1 + '@aws-sdk/util-user-agent-browser': 3.972.4 + '@aws-sdk/util-user-agent-node': 3.972.12 + '@smithy/config-resolver': 4.4.9 + '@smithy/core': 3.23.6 + '@smithy/eventstream-serde-browser': 4.2.10 + '@smithy/eventstream-serde-config-resolver': 4.3.10 + '@smithy/eventstream-serde-node': 4.2.10 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/hash-blob-browser': 4.2.11 + '@smithy/hash-node': 4.2.10 + '@smithy/hash-stream-node': 4.2.10 + '@smithy/invalid-dependency': 4.2.10 + '@smithy/md5-js': 4.2.10 + '@smithy/middleware-content-length': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-serde': 4.2.11 + '@smithy/middleware-stack': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/node-http-handler': 4.4.12 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.2 + '@smithy/util-defaults-mode-browser': 4.3.36 + '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 + '@smithy/util-waiter': 4.2.10 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.973.13': + dependencies: + '@aws-sdk/types': 3.973.2 + '@aws-sdk/xml-builder': 3.972.6 + '@smithy/core': 3.23.6 + '@smithy/node-config-provider': 4.3.10 + '@smithy/property-provider': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/signature-v4': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.972.1': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.11': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/types': 3.973.2 + '@smithy/property-provider': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.13': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/types': 3.973.2 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/node-http-handler': 4.4.12 + '@smithy/property-provider': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.15 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.11': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/credential-provider-env': 3.972.11 + '@aws-sdk/credential-provider-http': 3.972.13 + '@aws-sdk/credential-provider-login': 3.972.11 + '@aws-sdk/credential-provider-process': 3.972.11 + '@aws-sdk/credential-provider-sso': 3.972.11 + '@aws-sdk/credential-provider-web-identity': 3.972.11 + '@aws-sdk/nested-clients': 3.996.1 + '@aws-sdk/types': 3.973.2 + '@smithy/credential-provider-imds': 4.2.10 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.11': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/nested-clients': 3.996.1 + '@aws-sdk/types': 3.973.2 + '@smithy/property-provider': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.12': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.11 + '@aws-sdk/credential-provider-http': 3.972.13 + '@aws-sdk/credential-provider-ini': 3.972.11 + '@aws-sdk/credential-provider-process': 3.972.11 + '@aws-sdk/credential-provider-sso': 3.972.11 + '@aws-sdk/credential-provider-web-identity': 3.972.11 + '@aws-sdk/types': 3.973.2 + '@smithy/credential-provider-imds': 4.2.10 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.11': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/types': 3.973.2 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.11': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/nested-clients': 3.996.1 + '@aws-sdk/token-providers': 3.997.0 + '@aws-sdk/types': 3.973.2 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.11': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/nested-clients': 3.996.1 + '@aws-sdk/types': 3.973.2 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/lib-storage@3.997.0(@aws-sdk/client-s3@3.997.0)': + dependencies: + '@aws-sdk/client-s3': 3.997.0 + '@smithy/abort-controller': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/smithy-client': 4.12.0 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-bucket-endpoint@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-arn-parser': 3.972.2 + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.972.11': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.973.13 + '@aws-sdk/crc64-nvme': 3.972.1 + '@aws-sdk/types': 3.973.2 + '@smithy/is-array-buffer': 4.2.1 + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.13': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-arn-parser': 3.972.2 + '@smithy/core': 3.23.6 + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/signature-v4': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.13': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-endpoints': 3.996.1 + '@smithy/core': 3.23.6 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.996.1': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.13 + '@aws-sdk/middleware-host-header': 3.972.4 + '@aws-sdk/middleware-logger': 3.972.4 + '@aws-sdk/middleware-recursion-detection': 3.972.4 + '@aws-sdk/middleware-user-agent': 3.972.13 + '@aws-sdk/region-config-resolver': 3.972.4 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-endpoints': 3.996.1 + '@aws-sdk/util-user-agent-browser': 3.972.4 + '@aws-sdk/util-user-agent-node': 3.972.12 + '@smithy/config-resolver': 4.4.9 + '@smithy/core': 3.23.6 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/hash-node': 4.2.10 + '@smithy/invalid-dependency': 4.2.10 + '@smithy/middleware-content-length': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-serde': 4.2.11 + '@smithy/middleware-stack': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/node-http-handler': 4.4.12 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.2 + '@smithy/util-defaults-mode-browser': 4.3.36 + '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/config-resolver': 4.4.9 + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/s3-presigned-post@3.997.0': + dependencies: + '@aws-sdk/client-s3': 3.997.0 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-format-url': 3.972.4 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/signature-v4': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.1 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/s3-request-presigner@3.997.0': + dependencies: + '@aws-sdk/signature-v4-multi-region': 3.996.1 + '@aws-sdk/types': 3.973.2 + '@aws-sdk/util-format-url': 3.972.4 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.1': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.972.13 + '@aws-sdk/types': 3.973.2 + '@smithy/protocol-http': 5.3.10 + '@smithy/signature-v4': 5.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.997.0': + dependencies: + '@aws-sdk/core': 3.973.13 + '@aws-sdk/nested-clients': 3.996.1 + '@aws-sdk/types': 3.973.2 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.2': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.972.2': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.1': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-endpoints': 3.3.1 + tslib: 2.8.1 + + '@aws-sdk/util-format-url@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/querystring-builder': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.4': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.4': + dependencies: + '@aws-sdk/types': 3.973.2 + '@smithy/types': 4.13.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.972.12': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.13 + '@aws-sdk/types': 3.973.2 + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.6': + dependencies: + '@smithy/types': 4.13.0 + fast-xml-parser: 5.3.6 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.3': {} + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@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.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@0.2.3': {} + + '@colors/colors@1.6.0': {} + + '@dabh/diagnostics@2.0.8': + dependencies: + '@so-ric/colorspace': 1.1.6 + enabled: 2.0.0 + kuler: 2.0.0 + + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.4 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@faker-js/faker@8.4.1': {} + + '@graphql-tools/graphql-file-loader@8.1.9(graphql@16.13.0)': + dependencies: + '@graphql-tools/import': 7.1.9(graphql@16.13.0) + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + globby: 11.1.0 + graphql: 16.13.0 + tslib: 2.8.1 + unixify: 1.0.0 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/import@7.1.9(graphql@16.13.0)': + dependencies: + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + '@theguild/federation-composition': 0.21.3(graphql@16.13.0) + graphql: 16.13.0 + resolve-from: 5.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/load@8.1.8(graphql@16.13.0)': + dependencies: + '@graphql-tools/schema': 10.0.31(graphql@16.13.0) + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + graphql: 16.13.0 + p-limit: 3.1.0 + tslib: 2.8.1 + + '@graphql-tools/merge@8.3.1(graphql@16.13.0)': + dependencies: + '@graphql-tools/utils': 8.9.0(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-tools/merge@8.4.2(graphql@16.13.0)': + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-tools/merge@9.1.7(graphql@16.13.0)': + dependencies: + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-tools/mock@8.7.20(graphql@16.13.0)': + dependencies: + '@graphql-tools/schema': 9.0.19(graphql@16.13.0) + '@graphql-tools/utils': 9.2.1(graphql@16.13.0) + fast-json-stable-stringify: 2.1.0 + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-tools/schema@10.0.31(graphql@16.13.0)': + dependencies: + '@graphql-tools/merge': 9.1.7(graphql@16.13.0) + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-tools/schema@8.5.1(graphql@16.13.0)': + dependencies: + '@graphql-tools/merge': 8.3.1(graphql@16.13.0) + '@graphql-tools/utils': 8.9.0(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + value-or-promise: 1.0.11 + + '@graphql-tools/schema@9.0.19(graphql@16.13.0)': + dependencies: + '@graphql-tools/merge': 8.4.2(graphql@16.13.0) + '@graphql-tools/utils': 9.2.1(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + value-or-promise: 1.0.12 + + '@graphql-tools/utils@11.0.0(graphql@16.13.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-tools/utils@8.9.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-tools/utils@9.2.1(graphql@16.13.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-typed-document-node/core@3.2.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 + + '@grpc/grpc-js@1.14.3': + dependencies: + '@grpc/proto-loader': 0.8.0 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@grpc/proto-loader@0.8.0': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3 + minimatch: 3.1.4 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@ioredis/commands@1.5.0': + optional: true + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.2 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@25.3.0) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 25.3.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 25.3.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.10 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.29.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.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: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 25.3.0 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@josephg/resolvable@1.0.1': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@keycloak/keycloak-admin-client@26.2.4': + dependencies: + camelize-ts: 3.0.0 + url-join: 5.0.0 + url-template: 3.1.1 + + '@messageformat/core@3.4.0': + dependencies: + '@messageformat/date-skeleton': 1.1.0 + '@messageformat/number-skeleton': 1.2.0 + '@messageformat/parser': 5.1.1 + '@messageformat/runtime': 3.0.2 + make-plural: 7.5.0 + safe-identifier: 0.4.2 + + '@messageformat/date-skeleton@1.1.0': {} + + '@messageformat/number-skeleton@1.2.0': {} + + '@messageformat/parser@5.1.1': + dependencies: + moo: 0.5.2 + + '@messageformat/runtime@3.0.2': + dependencies: + make-plural: 7.5.0 + + '@newrelic/fn-inspect@4.4.0': + dependencies: + nan: 2.25.0 + node-gyp-build: 4.8.4 + prebuildify: 6.0.1 + optional: true + + '@newrelic/native-metrics@11.1.0': + dependencies: + nan: 2.25.0 + node-gyp-build: 4.8.4 + prebuildify: 6.0.1 + optional: true + + '@newrelic/security-agent@2.4.4': + dependencies: + axios: 1.13.5 + check-disk-space: 3.4.0 + content-type: 1.0.5 + cron: 3.5.0 + fast-safe-stringify: 2.1.1 + find-package-json: 1.2.0 + hash.js: 1.1.7 + html-entities: 2.6.0 + https-proxy-agent: 7.0.6 + is-invalid-path: 1.0.2 + js-yaml: 4.1.1 + jsonschema: 1.5.0 + lodash: 4.17.23 + log4js: 6.9.1 + pretty-bytes: 5.6.0 + request-ip: 3.3.0 + ringbufferjs: 2.0.0 + semver: 7.7.4 + unescape: 1.0.1 + unescape-js: 1.1.4 + uuid: 9.0.1 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@opentelemetry/api-logs@0.201.1': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/exporter-metrics-otlp-http@0.201.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.201.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.201.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-proto@0.201.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.201.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.201.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.201.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-exporter-base@0.201.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.201.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-transformer@0.201.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.201.1 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.201.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + + '@opentelemetry/resources@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/sdk-logs@0.201.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.201.1 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/semantic-conventions@1.39.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@prisma/prisma-fmt-wasm@4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085': + optional: true + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@rtsao/scc@1.1.0': {} + + '@s3pweb/keycloak-admin-client-cjs@26.2.4': + dependencies: + '@keycloak/keycloak-admin-client': 26.2.4 + + '@sinclair/typebox@0.27.10': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@smithy/abort-controller@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.2.2': + dependencies: + '@smithy/util-base64': 4.3.1 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.9': + dependencies: + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.1 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + tslib: 2.8.1 + + '@smithy/core@3.23.6': + dependencies: + '@smithy/middleware-serde': 4.2.11 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 + '@smithy/uuid': 1.1.1 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.10': + dependencies: + '@smithy/node-config-provider': 4.3.10 + '@smithy/property-provider': 4.2.10 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.10': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.1 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.10': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.10': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.10': + dependencies: + '@smithy/eventstream-codec': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.11': + dependencies: + '@smithy/protocol-http': 5.3.10 + '@smithy/querystring-builder': 4.2.10 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.2.11': + dependencies: + '@smithy/chunked-blob-reader': 5.2.1 + '@smithy/chunked-blob-reader-native': 4.2.2 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.10': + dependencies: + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.20': + dependencies: + '@smithy/core': 3.23.6 + '@smithy/middleware-serde': 4.2.11 + '@smithy/node-config-provider': 4.3.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-middleware': 4.2.10 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.37': + dependencies: + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/service-error-classification': 4.2.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/uuid': 1.1.1 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.11': + dependencies: + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.10': + dependencies: + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.4.12': + dependencies: + '@smithy/abort-controller': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/querystring-builder': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + '@smithy/util-uri-escape': 4.2.1 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + + '@smithy/shared-ini-file-loader@4.4.5': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.10': + dependencies: + '@smithy/is-array-buffer': 4.2.1 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-uri-escape': 4.2.1 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.0': + dependencies: + '@smithy/core': 3.23.6 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-stack': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.15 + tslib: 2.8.1 + + '@smithy/types@4.13.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.10': + dependencies: + '@smithy/querystring-parser': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.1': + dependencies: + '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.1': + dependencies: + '@smithy/is-array-buffer': 4.2.1 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.36': + dependencies: + '@smithy/property-provider': 4.2.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.39': + dependencies: + '@smithy/config-resolver': 4.4.9 + '@smithy/credential-provider-imds': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/property-provider': 4.2.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.3.1': + dependencies: + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.10': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.10': + dependencies: + '@smithy/service-error-classification': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.15': + dependencies: + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/node-http-handler': 4.4.12 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-hex-encoding': 4.2.1 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.1': + dependencies: + '@smithy/util-buffer-from': 4.2.1 + tslib: 2.8.1 + + '@smithy/util-waiter@4.2.10': + dependencies: + '@smithy/abort-controller': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.1': + dependencies: + tslib: 2.8.1 + + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.3 + text-hex: 1.0.0 + + '@supercharge/request-ip@1.2.0': {} + + '@testim/chrome-version@1.1.4': + optional: true + + '@theguild/federation-composition@0.21.3(graphql@16.13.0)': + dependencies: + constant-case: 3.0.4 + debug: 4.4.3 + graphql: 16.13.0 + json5: 2.2.3 + lodash.sortby: 4.7.0 + transitivePeerDependencies: + - supports-color + + '@tootallnate/quickjs-emscripten@0.23.0': + optional: true + + '@types/accepts@1.3.7': + dependencies: + '@types/node': 25.3.0 + + '@types/amqp-connection-manager@2.0.12': + dependencies: + '@types/amqplib': 0.10.8 + + '@types/amqplib@0.10.8': + dependencies: + '@types/node': 25.3.0 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/body-parser@1.19.2': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 25.3.0 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 25.3.0 + + '@types/busboy@1.5.4': + dependencies: + '@types/node': 25.3.0 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 25.3.0 + + '@types/cors@2.8.12': {} + + '@types/express-serve-static-core@4.17.31': + dependencies: + '@types/node': 25.3.0 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + + '@types/express-serve-static-core@4.19.8': + dependencies: + '@types/node': 25.3.0 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@4.17.14': + dependencies: + '@types/body-parser': 1.19.2 + '@types/express-serve-static-core': 4.17.31 + '@types/qs': 6.14.0 + '@types/serve-static': 2.2.0 + + '@types/express@4.17.25': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.8 + '@types/qs': 6.14.0 + '@types/serve-static': 1.15.10 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 25.3.0 + + '@types/http-errors@2.0.5': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/json5@0.0.29': {} + + '@types/jsonwebtoken@9.0.10': + dependencies: + '@types/ms': 2.1.0 + '@types/node': 25.3.0 + + '@types/long@4.0.2': {} + + '@types/luxon@3.4.2': {} + + '@types/mime@1.3.5': {} + + '@types/ms@2.1.0': {} + + '@types/node-cache@4.2.5': + dependencies: + node-cache: 5.1.2 + + '@types/node@10.17.60': {} + + '@types/node@25.3.0': + dependencies: + undici-types: 7.18.2 + + '@types/object-path@0.11.4': {} + + '@types/qs@6.14.0': {} + + '@types/ramda@0.31.1': + dependencies: + types-ramda: 0.31.0 + + '@types/range-parser@1.2.7': {} + + '@types/redis@2.8.32': + dependencies: + '@types/node': 25.3.0 + + '@types/send@0.17.6': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 25.3.0 + + '@types/send@1.2.1': + dependencies: + '@types/node': 25.3.0 + + '@types/serve-static@1.15.10': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 25.3.0 + '@types/send': 0.17.6 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 25.3.0 + + '@types/sha1@1.1.5': + dependencies: + '@types/node': 25.3.0 + + '@types/stack-utils@2.0.3': {} + + '@types/triple-beam@1.3.5': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.3.0 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 25.3.0 + optional: true + + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/type-utils': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.56.1 + eslint: 8.57.1 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.3 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + eslint: 8.57.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) + '@typescript-eslint/types': 8.56.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + + '@typescript-eslint/scope-manager@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.56.1(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + debug: 4.4.3 + eslint: 8.57.1 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@6.21.0': {} + + '@typescript-eslint/types@8.56.1': {} + + '@typescript-eslint/typescript-estree@6.21.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.7.4 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + eslint: 8.57.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + eslint-visitor-keys: 5.0.1 + + '@tyriar/fibonacci-heap@2.0.9': {} + + '@ungap/structured-clone@1.3.0': {} + + '@whatwg-node/promise-helpers@1.3.2': + dependencies: + tslib: 2.8.1 + + '@wry/caches@1.0.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@wry/context@0.7.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@wry/equality@0.5.7': + dependencies: + tslib: 2.8.1 + optional: true + + '@wry/trie@0.5.0': + dependencies: + tslib: 2.8.1 + optional: true + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-import-attributes@1.9.5(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + agent-base@7.1.4: {} + + agentkeepalive@3.5.3: + dependencies: + humanize-ms: 1.2.1 + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + amqp-connection-manager@4.1.15(amqplib@0.10.9): + dependencies: + amqplib: 0.10.9 + promise-breaker: 6.0.0 + + amqplib@0.10.9: + dependencies: + buffer-more-ints: 1.0.0 + url-parse: 1.5.10 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@2.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@2.2.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + apollo-datasource@3.3.2: + dependencies: + '@apollo/utils.keyvaluecache': 1.0.2 + apollo-server-env: 4.2.1 + transitivePeerDependencies: + - encoding + + apollo-reporting-protobuf@3.4.0: + dependencies: + '@apollo/protobufjs': 1.2.6 + + apollo-server-core@3.13.0(graphql@16.13.0): + dependencies: + '@apollo/utils.keyvaluecache': 1.0.2 + '@apollo/utils.logger': 1.0.1 + '@apollo/utils.usagereporting': 1.0.1(graphql@16.13.0) + '@apollographql/apollo-tools': 0.5.4(graphql@16.13.0) + '@apollographql/graphql-playground-html': 1.6.29 + '@graphql-tools/mock': 8.7.20(graphql@16.13.0) + '@graphql-tools/schema': 8.5.1(graphql@16.13.0) + '@josephg/resolvable': 1.0.1 + apollo-datasource: 3.3.2 + apollo-reporting-protobuf: 3.4.0 + apollo-server-env: 4.2.1 + apollo-server-errors: 3.3.1(graphql@16.13.0) + apollo-server-plugin-base: 3.7.2(graphql@16.13.0) + apollo-server-types: 3.8.0(graphql@16.13.0) + async-retry: 1.3.3 + fast-json-stable-stringify: 2.1.0 + graphql: 16.13.0 + graphql-tag: 2.12.6(graphql@16.13.0) + loglevel: 1.9.2 + lru-cache: 6.0.0 + node-abort-controller: 3.1.1 + sha.js: 2.4.12 + uuid: 9.0.1 + whatwg-mimetype: 3.0.0 + transitivePeerDependencies: + - encoding + + apollo-server-env@4.2.1: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + apollo-server-errors@3.3.1(graphql@16.13.0): + dependencies: + graphql: 16.13.0 + + apollo-server-express@3.13.0(express@4.22.1)(graphql@16.13.0): + dependencies: + '@types/accepts': 1.3.7 + '@types/body-parser': 1.19.2 + '@types/cors': 2.8.12 + '@types/express': 4.17.14 + '@types/express-serve-static-core': 4.17.31 + accepts: 1.3.8 + apollo-server-core: 3.13.0(graphql@16.13.0) + apollo-server-types: 3.8.0(graphql@16.13.0) + body-parser: 1.20.4 + cors: 2.8.6 + express: 4.22.1 + graphql: 16.13.0 + parseurl: 1.3.3 + transitivePeerDependencies: + - encoding + - supports-color + + apollo-server-plugin-base@3.7.2(graphql@16.13.0): + dependencies: + apollo-server-types: 3.8.0(graphql@16.13.0) + graphql: 16.13.0 + transitivePeerDependencies: + - encoding + + apollo-server-types@3.8.0(graphql@16.13.0): + dependencies: + '@apollo/utils.keyvaluecache': 1.0.2 + '@apollo/utils.logger': 1.0.1 + apollo-reporting-protobuf: 3.4.0 + apollo-server-env: 4.2.1 + graphql: 16.13.0 + transitivePeerDependencies: + - encoding + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-flatten@1.1.1: {} + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array-union@2.1.0: {} + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + arrify@2.0.1: {} + + asn1.js@5.4.1: + dependencies: + bn.js: 4.12.3 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + optional: true + + async-function@1.0.0: {} + + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + auto-parse@1.8.0: + dependencies: + typpy: 2.3.11 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + aws-sdk@2.1693.0: + dependencies: + buffer: 4.9.2 + events: 1.1.1 + ieee754: 1.1.13 + jmespath: 0.16.0 + querystring: 0.2.0 + sax: 1.2.1 + url: 0.10.3 + util: 0.12.5 + uuid: 8.0.0 + xml2js: 0.6.2 + + aws-ssl-profiles@1.1.2: {} + + axios@1.13.5: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-jest@29.7.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + + babel-preset-jest@29.6.3(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + + babel-runtime@6.26.0: + dependencies: + core-js: 2.6.12 + regenerator-runtime: 0.11.1 + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.0: {} + + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + + basic-ftp@5.2.0: + optional: true + + bignumber.js@9.3.1: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + optional: true + + bn.js@4.12.3: {} + + body-parser@1.20.4: + 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.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.2 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + boolify@1.0.1: {} + + bowser@2.14.1: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.3: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001774 + electron-to-chromium: 1.5.302 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-crc32@0.2.13: + optional: true + + buffer-equal-constant-time@1.0.1: {} + + buffer-from@1.1.2: {} + + buffer-more-ints@1.0.0: {} + + buffer@4.9.2: + dependencies: + base64-js: 1.5.1 + ieee754: 1.1.13 + isarray: 1.0.0 + + buffer@5.6.0: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + optional: true + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase-keys@4.2.0: + dependencies: + camelcase: 4.1.0 + map-obj: 2.0.0 + quick-lru: 1.1.0 + + camelcase-keys@9.1.3: + dependencies: + camelcase: 8.0.0 + map-obj: 5.0.0 + quick-lru: 6.1.2 + type-fest: 4.41.0 + + camelcase@4.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + camelcase@8.0.0: {} + + camelize-ts@3.0.0: {} + + caniuse-lite@1.0.30001774: {} + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + charenc@0.0.2: {} + + check-disk-space@3.4.0: {} + + chownr@1.1.4: + optional: true + + chromedriver@146.0.0: + dependencies: + '@testim/chrome-version': 1.1.4 + axios: 1.13.5 + compare-versions: 6.1.1 + extract-zip: 2.0.1 + proxy-agent: 6.5.0 + proxy-from-env: 2.0.0 + tcp-port-used: 1.0.2 + transitivePeerDependencies: + - debug + - supports-color + optional: true + + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.3: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@2.1.2: {} + + cluster-key-slot@1.1.2: + optional: true + + co@4.6.0: {} + + collect-v8-coverage@1.0.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-convert@3.1.3: + dependencies: + color-name: 2.1.0 + + color-name@1.1.4: {} + + color-name@2.1.0: {} + + color-string@2.1.4: + dependencies: + color-name: 2.1.0 + + color@5.0.3: + dependencies: + color-convert: 3.1.3 + color-string: 2.1.4 + + colorette@2.0.19: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@10.0.1: {} + + commander@2.20.3: {} + + common-tags@1.8.2: {} + + compare-versions@6.1.1: + optional: true + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1: + dependencies: + 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 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + concat-stream@2.0.0: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + + confusing-browser-globals@1.0.11: {} + + constant-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case: 2.0.2 + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.7: {} + + cookie@0.7.2: {} + + core-js@2.6.12: {} + + core-js@3.48.0: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + create-jest@29.7.0(@types/node@25.3.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@25.3.0) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + cron@3.5.0: + dependencies: + '@types/luxon': 3.4.2 + luxon: 3.5.0 + + cross-inspect@1.0.1: + dependencies: + tslib: 2.8.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypt@0.0.2: {} + + cssfilter@0.0.10: {} + + data-uri-to-buffer@4.0.1: {} + + data-uri-to-buffer@6.0.2: + optional: true + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + date-format@4.0.14: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.3.1: + dependencies: + ms: 2.1.2 + optional: true + + debug@4.3.4: + dependencies: + ms: 2.1.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + dedent@1.7.1: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + optional: true + + delayed-stream@1.0.0: {} + + denque@1.5.1: {} + + denque@2.1.0: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-newline@3.1.0: {} + + diff-sequences@29.6.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dotenv-extended@2.9.0: + dependencies: + auto-parse: 1.8.0 + camelcase: 5.3.1 + cross-spawn: 7.0.6 + dotenv: 8.6.0 + + dotenv@4.0.0: {} + + dotenv@8.6.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + eastasianwidth@0.2.0: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + elasticsearch@15.5.0: + dependencies: + agentkeepalive: 3.5.3 + chalk: 1.1.3 + lodash: 4.17.23 + + electron-to-chromium@1.5.302: {} + + elliptic@6.6.1: + dependencies: + bn.js: 4.12.3 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enabled@2.0.0: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + optional: true + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + optional: true + + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1): + dependencies: + confusing-browser-globals: 1.0.11 + eslint: 8.57.1 + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) + object.assign: 4.1.7 + object.entries: 1.1.9 + semver: 6.3.1 + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.4 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + 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.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.4 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + esm@3.2.25: {} + + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-stream@3.3.4: + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + + events@1.1.1: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + 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.14.2 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + optional: true + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-safe-stringify@2.1.1: {} + + fast-xml-parser@5.3.6: + dependencies: + strnum: 2.1.2 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + optional: true + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fecha@4.2.3: {} + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.2: + dependencies: + 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 + transitivePeerDependencies: + - supports-color + + find-package-json@1.2.0: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.3: {} + + fn.name@1.1.0: {} + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.5: + 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 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + from@0.1.7: {} + + fs-capacitor@8.0.0: {} + + fs-constants@1.0.0: + optional: true + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.name@1.0.14: + dependencies: + noop6: 1.0.10 + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + 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@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stdin@8.0.0: {} + + get-stream@5.2.0: + dependencies: + pump: 3.0.3 + optional: true + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-uri@6.0.5: + dependencies: + basic-ftp: 5.2.0 + data-uri-to-buffer: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + getopts@2.3.0: {} + + git-up@7.0.0: + dependencies: + is-ssh: 1.4.1 + parse-url: 8.1.0 + + git-url-parse@13.1.1: + dependencies: + git-up: 7.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.7 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.4 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + graphql-iso-date@3.6.1(graphql@16.13.0): + dependencies: + graphql: 16.13.0 + + graphql-redis-subscriptions@2.7.0(graphql-subscriptions@1.2.1(graphql@16.13.0)): + dependencies: + graphql-subscriptions: 1.2.1(graphql@16.13.0) + optionalDependencies: + ioredis: 5.9.3 + transitivePeerDependencies: + - supports-color + + graphql-sse@2.6.0(graphql@16.13.0): + dependencies: + graphql: 16.13.0 + + graphql-subscriptions@1.2.1(graphql@16.13.0): + dependencies: + graphql: 16.13.0 + iterall: 1.3.0 + + graphql-tag@2.12.6(graphql@16.13.0): + dependencies: + graphql: 16.13.0 + tslib: 2.8.1 + + graphql-tools@9.0.26(graphql-ws@5.16.0(graphql@16.13.0))(graphql@16.13.0)(rxjs@7.8.2): + dependencies: + '@graphql-tools/schema': 10.0.31(graphql@16.13.0) + graphql: 16.13.0 + tslib: 2.8.1 + optionalDependencies: + '@apollo/client': 4.0.13(graphql-ws@5.16.0(graphql@16.13.0))(graphql@16.13.0)(rxjs@7.8.2) + transitivePeerDependencies: + - graphql-ws + - react + - react-dom + - rxjs + - subscriptions-transport-ws + + graphql-type-json@0.3.2(graphql@16.13.0): + dependencies: + graphql: 16.13.0 + + graphql-upload@17.0.0(@types/express@4.17.25)(graphql@16.13.0): + dependencies: + '@types/busboy': 1.5.4 + '@types/node': 25.3.0 + '@types/object-path': 0.11.4 + busboy: 1.6.0 + fs-capacitor: 8.0.0 + graphql: 16.13.0 + http-errors: 2.0.1 + object-path: 0.11.8 + optionalDependencies: + '@types/express': 4.17.25 + + graphql-ws@5.16.0(graphql@16.13.0): + dependencies: + graphql: 16.13.0 + + graphql@16.13.0: {} + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + html-entities@2.6.0: {} + + html-escaper@2.0.2: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.1.13: {} + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-in-the-middle@1.15.0: + dependencies: + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + interpret@2.2.0: {} + + ioredis@5.9.3: + dependencies: + '@ioredis/commands': 1.5.0 + cluster-key-slot: 1.1.2 + debug: 4.4.3 + 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 + transitivePeerDependencies: + - supports-color + optional: true + + ip-address@10.1.0: + optional: true + + ip-regex@4.3.0: + optional: true + + ipaddr.js@1.9.1: {} + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extendable@0.1.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-invalid-path@1.0.2: {} + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-property@1.0.2: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-ssh@1.4.1: + dependencies: + protocols: 2.0.2 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-url@1.2.4: + optional: true + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is2@2.0.9: + dependencies: + deep-is: 0.1.4 + ip-regex: 4.3.0 + is-url: 1.2.4 + optional: true + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterall@1.3.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.1 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@25.3.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@25.3.0) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@25.3.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@25.3.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 25.3.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 25.3.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.11 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.3 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.3.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 25.3.0 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@25.3.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@25.3.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jmespath@0.16.0: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-safe@5.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonschema@1.5.0: {} + + jsonwebtoken@8.5.1: + dependencies: + jws: 3.2.3 + 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.1.1 + ms: 2.1.3 + semver: 5.7.2 + + jwa@1.4.2: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jwk-to-pem@2.0.7: + dependencies: + asn1.js: 5.4.1 + elliptic: 6.6.1 + safe-buffer: 5.2.1 + + jws@3.2.3: + dependencies: + jwa: 1.4.2 + safe-buffer: 5.2.1 + + keycloak-connect@26.1.1: + dependencies: + jwk-to-pem: 2.0.7 + optionalDependencies: + chromedriver: 146.0.0 + transitivePeerDependencies: + - debug + - supports-color + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + knex@3.1.0(mysql2@3.18.0(@types/node@25.3.0)): + dependencies: + colorette: 2.0.19 + commander: 10.0.1 + debug: 4.3.4 + escalade: 3.2.0 + esm: 3.2.25 + get-package-type: 0.1.0 + getopts: 2.3.0 + interpret: 2.2.0 + lodash: 4.17.23 + pg-connection-string: 2.6.2 + rechoir: 0.8.0 + resolve-from: 5.0.0 + tarn: 3.0.2 + tildify: 2.0.0 + optionalDependencies: + mysql2: 3.18.0(@types/node@25.3.0) + transitivePeerDependencies: + - supports-color + + kuler@2.0.0: {} + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.camelcase@4.3.0: {} + + lodash.defaults@4.2.0: + optional: true + + lodash.includes@4.3.0: {} + + lodash.isarguments@3.1.0: + optional: true + + 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.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash.sortby@4.7.0: {} + + lodash@4.17.23: {} + + log4js@6.9.1: + dependencies: + date-format: 4.0.14 + debug: 4.4.3 + flatted: 3.3.3 + rfdc: 1.4.1 + streamroller: 3.1.5 + transitivePeerDependencies: + - supports-color + + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + + loglevel-colored-level-prefix@1.0.0: + dependencies: + chalk: 1.1.3 + loglevel: 1.9.2 + + loglevel@1.9.2: {} + + lokka@1.7.0: + dependencies: + babel-runtime: 6.26.0 + uuid: 2.0.3 + + long@4.0.0: {} + + long@5.3.2: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lru-cache@7.13.1: {} + + lru-cache@7.18.3: + optional: true + + lru.min@1.1.4: {} + + luxon@3.5.0: {} + + make-dir@4.0.0: + dependencies: + semver: 7.7.4 + + make-error@1.3.6: {} + + make-plural@7.5.0: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + map-obj@2.0.0: {} + + map-obj@5.0.0: {} + + map-stream@0.1.0: {} + + mariadb@3.5.1(@types/node@25.3.0): + dependencies: + denque: 2.1.0 + iconv-lite: 0.7.2 + lru-cache: 10.4.3 + optionalDependencies: + '@types/node': 25.3.0 + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + merge-descriptors@1.0.3: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + mimic-fn@2.1.0: {} + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.3 + + minimatch@3.1.4: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.7: + dependencies: + brace-expansion: 5.0.3 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + mkdirp-classic@0.5.3: + optional: true + + module-details-from-path@1.0.4: {} + + moment@2.30.1: {} + + moo@0.5.2: {} + + morgan@1.10.1: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.1.0 + transitivePeerDependencies: + - supports-color + + ms@2.0.0: {} + + ms@2.1.2: {} + + ms@2.1.3: {} + + mysql2@3.18.0(@types/node@25.3.0): + dependencies: + '@types/node': 25.3.0 + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.2 + long: 5.3.2 + lru.min: 1.1.4 + named-placeholders: 1.1.6 + sql-escaper: 1.3.3 + + named-placeholders@1.1.6: + dependencies: + lru.min: 1.1.4 + + nan@2.25.0: + optional: true + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + neo-async@2.6.2: {} + + netmask@2.0.2: + optional: true + + newrelic@12.25.1: + dependencies: + '@grpc/grpc-js': 1.14.3 + '@grpc/proto-loader': 0.7.15 + '@newrelic/security-agent': 2.4.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-proto': 0.201.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@tyriar/fibonacci-heap': 2.0.9 + concat-stream: 2.0.0 + https-proxy-agent: 7.0.6 + import-in-the-middle: 1.15.0 + json-bigint: 1.0.0 + json-stringify-safe: 5.0.1 + module-details-from-path: 1.0.4 + readable-stream: 3.6.2 + require-in-the-middle: 7.5.2 + semver: 7.7.4 + winston-transport: 4.9.0 + optionalDependencies: + '@newrelic/fn-inspect': 4.4.0 + '@newrelic/native-metrics': 11.1.0 + '@prisma/prisma-fmt-wasm': 4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-abi@3.87.0: + dependencies: + semver: 7.7.4 + optional: true + + node-abort-controller@3.1.1: {} + + node-cache@5.1.2: + dependencies: + clone: 2.1.2 + + node-cleanup@2.1.2: {} + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-gyp-build@4.8.4: + optional: true + + node-int64@0.4.0: {} + + node-releases@2.0.27: {} + + noop6@1.0.10: {} + + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + + normalize-path@3.0.0: {} + + npm-run-path@3.1.0: + dependencies: + path-key: 3.1.1 + optional: true + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object-path@0.11.8: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + optimism@0.18.1: + dependencies: + '@wry/caches': 1.0.1 + '@wry/context': 0.7.4 + '@wry/trie': 0.5.0 + tslib: 2.8.1 + optional: true + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-try@2.2.0: {} + + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.4 + debug: 4.4.3 + get-uri: 6.0.5 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + optional: true + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + optional: true + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-path@7.1.0: + dependencies: + protocols: 2.0.2 + + parse-url@8.1.0: + dependencies: + parse-path: 7.1.0 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-to-regexp@0.1.12: {} + + path-type@4.0.0: {} + + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + + pend@1.2.0: + optional: true + + pg-connection-string@2.6.2: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + possible-typed-array-names@1.1.0: {} + + prebuildify@6.0.1: + dependencies: + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + node-abi: 3.87.0 + npm-run-path: 3.1.0 + pump: 3.0.3 + tar-fs: 2.1.4 + optional: true + + prelude-ls@1.2.1: {} + + prettier-eslint-cli@8.0.1(prettier-eslint@16.4.2(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@messageformat/core': 3.4.0 + '@prettier/eslint': prettier-eslint@16.4.2(typescript@5.9.3) + arrify: 2.0.1 + boolify: 1.0.1 + camelcase-keys: 9.1.3 + chalk: 4.1.2 + common-tags: 1.8.2 + core-js: 3.48.0 + eslint: 8.57.1 + find-up: 5.0.0 + get-stdin: 8.0.0 + glob: 10.5.0 + ignore: 5.3.2 + indent-string: 4.0.0 + lodash.memoize: 4.1.2 + loglevel-colored-level-prefix: 1.0.0 + rxjs: 7.8.2 + yargs: 17.7.2 + optionalDependencies: + prettier-eslint: 16.4.2(typescript@5.9.3) + transitivePeerDependencies: + - prettier-plugin-svelte + - supports-color + - svelte-eslint-parser + - typescript + + prettier-eslint@16.4.2(typescript@5.9.3): + dependencies: + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + common-tags: 1.8.2 + dlv: 1.1.3 + eslint: 8.57.1 + indent-string: 4.0.0 + lodash.merge: 4.6.2 + loglevel-colored-level-prefix: 1.0.0 + prettier: 3.8.1 + pretty-format: 29.7.0 + require-relative: 0.8.7 + tslib: 2.8.1 + vue-eslint-parser: 9.4.3(eslint@8.57.1) + transitivePeerDependencies: + - supports-color + - typescript + + prettier@3.8.1: {} + + pretty-bytes@5.6.0: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + promise-breaker@6.0.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 25.3.0 + long: 5.3.2 + + protocols@2.0.2: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + optional: true + + proxy-from-env@1.1.0: {} + + proxy-from-env@2.0.0: + optional: true + + ps-tree@1.2.0: + dependencies: + event-stream: 3.3.4 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + optional: true + + punycode@1.3.2: {} + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + qs@6.14.2: + dependencies: + side-channel: 1.1.0 + + querystring@0.2.0: {} + + querystringify@2.2.0: {} + + queue-microtask@1.2.3: {} + + quick-lru@1.1.0: {} + + quick-lru@6.1.2: {} + + ramda@0.32.0: {} + + range-parser@1.2.1: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + react-is@18.3.1: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + rechoir@0.8.0: + dependencies: + resolve: 1.22.11 + + redis-commands@1.7.0: {} + + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + + redis@3.1.2: + dependencies: + denque: 1.5.1 + redis-commands: 1.7.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerator-runtime@0.11.1: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + remove-trailing-separator@1.1.0: {} + + request-ip@3.3.0: {} + + require-directory@2.1.1: {} + + require-in-the-middle@7.5.2: + dependencies: + debug: 4.4.3 + module-details-from-path: 1.0.4 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + require-relative@0.8.7: {} + + requires-port@1.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + ringbufferjs@2.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-identifier@0.4.2: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + sax@1.2.1: {} + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.7.4: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + 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: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + sha1@1.1.1: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + smart-buffer@4.2.0: + optional: true + + snakecase-keys@1.2.0: + dependencies: + map-obj: 2.0.0 + to-snake-case: 0.1.2 + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + optional: true + + socks@2.8.7: + dependencies: + ip-address: 10.1.0 + smart-buffer: 4.2.0 + optional: true + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + split@0.3.3: + dependencies: + through: 2.3.8 + + sprintf-js@1.0.3: {} + + sql-escaper@1.3.3: {} + + stack-trace@0.0.10: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + standard-as-callback@2.1.0: + optional: true + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + stream-combiner@0.0.4: + dependencies: + duplexer: 0.1.2 + + streamroller@3.1.5: + dependencies: + date-format: 4.0.14 + debug: 4.4.3 + fs-extra: 8.1.0 + transitivePeerDependencies: + - supports-color + + streamsearch@1.1.0: {} + + string-argv@0.3.2: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string.fromcodepoint@0.2.1: {} + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + strnum@2.1.2: {} + + supports-color@2.0.0: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + optional: true + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + optional: true + + tarn@3.0.2: {} + + tcp-port-used@1.0.2: + dependencies: + debug: 4.3.1 + is2: 2.0.9 + transitivePeerDependencies: + - supports-color + optional: true + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.4 + + text-hex@1.0.0: {} + + text-table@0.2.0: {} + + through@2.3.8: {} + + tildify@2.0.0: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tmpl@1.0.5: {} + + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + + to-no-case@0.1.1: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + to-snake-case@0.1.2: + dependencies: + to-space-case: 0.1.2 + + to-space-case@0.1.2: + dependencies: + to-no-case: 0.1.1 + + toidentifier@1.0.1: {} + + tr46@0.0.3: {} + + triple-beam@1.4.1: {} + + ts-api-utils@1.4.3(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@25.3.0))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.8 + jest: 29.7.0(@types/node@25.3.0) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.4 + type-fest: 4.41.0 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + jest-util: 29.7.0 + + ts-toolbelt@9.6.0: {} + + tsc-watch@6.3.1(typescript@5.9.3): + dependencies: + cross-spawn: 7.0.6 + node-cleanup: 2.1.2 + ps-tree: 1.2.0 + string-argv: 0.3.2 + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typedarray@0.0.6: {} + + types-ramda@0.31.0: + dependencies: + ts-toolbelt: 9.6.0 + + typescript@5.9.3: {} + + typpy@2.3.11: + dependencies: + function.name: 1.0.14 + + uglify-js@3.19.3: + optional: true + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@7.18.2: {} + + unescape-js@1.1.4: + dependencies: + string.fromcodepoint: 0.2.1 + + unescape@1.0.1: + dependencies: + extend-shallow: 2.0.1 + + universalify@0.1.2: {} + + unixify@1.0.0: + dependencies: + normalize-path: 2.1.1 + + unpipe@1.0.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + upper-case@2.0.2: + dependencies: + tslib: 2.8.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-join@5.0.0: {} + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + url-template@3.1.1: {} + + url@0.10.3: + dependencies: + punycode: 1.3.2 + querystring: 0.2.0 + + util-deprecate@1.0.2: {} + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.20 + + utils-merge@1.0.1: {} + + uuid4@1.1.4: {} + + uuid@2.0.3: {} + + uuid@8.0.0: {} + + uuid@9.0.1: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validator@13.15.26: {} + + value-or-promise@1.0.11: {} + + value-or-promise@1.0.12: {} + + vary@1.1.2: {} + + vue-eslint-parser@9.4.3(eslint@8.57.1): + dependencies: + debug: 4.4.3 + eslint: 8.57.1 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + lodash: 4.17.23 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-mimetype@3.0.0: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + 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 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.19.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.8 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + + word-wrap@1.2.5: {} + + wordwrap@1.0.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + ws@8.19.0: {} + + xml2js@0.6.2: + dependencies: + sax: 1.2.1 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + xss@1.0.15: + dependencies: + commander: 2.20.3 + cssfilter: 0.0.10 + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + optional: true + + yocto-queue@0.1.0: {} diff --git a/services/api/src/apolloServer.js b/services/api/src/apolloServer.js index 0122b1b5ab..5394c5d957 100644 --- a/services/api/src/apolloServer.js +++ b/services/api/src/apolloServer.js @@ -1,9 +1,11 @@ +import { makeExecutableSchema } from '@graphql-tools/schema'; + const R = require('ramda'); const { ApolloServer, AuthenticationError, } = require('apollo-server-express'); -const gql = require('graphql-tag'); +const { parse: gqlParse } = require('graphql'); const newrelic = require('newrelic'); const { decode } = require('jsonwebtoken'); const { getConfigFromEnv } = require('./util/config'); @@ -13,7 +15,7 @@ const { getCredentialsForLegacyToken, getGrantForKeycloakToken, legacyHasPermission, - keycloakHasPermission + keycloakHasPermission, } = require('./util/auth'); const { sqlClientPool } = require('./clients/sqlClient'); const esClient = require('./clients/esClient'); @@ -27,9 +29,6 @@ const User = require('./models/user'); const Group = require('./models/group'); const Environment = require('./models/environment'); -import { makeExecutableSchema } from '@graphql-tools/schema'; - - let schema; let apolloServer; @@ -41,16 +40,16 @@ export const getGrantOrLegacyCredsFromToken = async token => { const legacyCredentials = await getCredentialsForLegacyToken(token); const { sub, iss } = legacyCredentials; - const username = sub ? sub : 'unknown'; - const source = iss ? iss : 'unknown'; + const username = sub || 'unknown'; + const source = iss || 'unknown'; userActivityLogger.user_auth( `Authentication granted for '${username}' from '${source}'`, - { user: legacyCredentials } + { user: legacyCredentials }, ); return { grant: null, - legacyCredentials + legacyCredentials, }; } catch (e) { throw new AuthenticationError(`Legacy token invalid: ${e.message}`); @@ -63,27 +62,27 @@ export const getGrantOrLegacyCredsFromToken = async token => { const { azp: source, preferred_username, - email + email, } = grant.access_token.content; - const username = preferred_username ? preferred_username : 'unknown'; + const username = preferred_username || 'unknown'; userActivityLogger.user_auth( `Authentication granted for '${username} (${ - email ? email : 'unknown' + email || 'unknown' })' from '${source}'`, - { user: grant ? grant.access_token.content : null } + { user: grant ? grant.access_token.content : null }, ); } return { grant, - legacyCredentials: null + legacyCredentials: null, }; } catch (e) { throw new AuthenticationError(`Keycloak token invalid: ${e.message}`); } } else { - throw new AuthenticationError(`Bearer token unrecognized`); + throw new AuthenticationError('Bearer token unrecognized'); } }; @@ -96,38 +95,38 @@ async function initCheck() { // Fixes schema creation to allow undefined in resolve schema = makeExecutableSchema({ typeDefs, - resolvers: resolvers, + resolvers, allowUndefinedInResolve: true, resolverValidationOptions: { requireResolversForArgs: false, requireResolversForNonScalar: false, requireResolversForAllFields: false, - } + }, }); apolloServer = new ApolloServer({ - schema: schema, + schema, debug: getConfigFromEnv('NODE_ENV') === 'development', introspection: true, csrfPrevention: { - requestHeaders: ['x-apollo-operation-name', 'apollo-require-preflight', 'content-type'] + requestHeaders: ['x-apollo-operation-name', 'apollo-require-preflight', 'content-type'], }, cache: 'bounded', parseOptions: { - maxTokens: 50000 + maxTokens: 50000, }, context: async ({ req, connection }) => { // Websocket requests if (connection) { // onConnect must always provide connection.context. return { - ...connection.context + ...connection.context, }; } // HTTP requests if (!connection) { - const keycloakAdminClient = await getKeycloakAdminClient(); + const keycloakAdminClient = await getKeycloakAdminClient(); const modelClients = { sqlClientPool, @@ -139,38 +138,38 @@ async function initCheck() { let serviceAccount = {}; // if this is a user request, get the users keycloak groups too, do this one to reduce the number of times it is called elsewhere // legacy accounts don't do this - let keycloakUsersGroups = [] - let groupRoleProjectIds = [] - const keycloakGrant = req.kauth ? req.kauth.grant : null - let legacyGrant = req.legacyCredentials ? req.legacyCredentials : null - let platformOwner = false - let platformViewer = false + let keycloakUsersGroups = []; + let groupRoleProjectIds = []; + const keycloakGrant = req.kauth ? req.kauth.grant : null; + const legacyGrant = req.legacyCredentials ? req.legacyCredentials : null; + let platformOwner = false; + let platformViewer = false; if (keycloakGrant) { // get all the users keycloak groups, do this early to reduce the number of times this is called otherwise try { keycloakUsersGroups = await User.User(modelClients).getAllGroupsForUser(keycloakGrant.access_token.content.sub); serviceAccount = await keycloakGrantManager.obtainFromClientCredentials(); currentUser = await User.User(modelClients).loadUserById(keycloakGrant.access_token.content.sub); - const userRoleMapping = await keycloakAdminClient.users.listRealmRoleMappings({id: currentUser.id}) + const userRoleMapping = await keycloakAdminClient.users.listRealmRoleMappings({ id: currentUser.id }); for (const role of userRoleMapping) { - if (role.name == "platform-owner") { - platformOwner = true + if (role.name === 'platform-owner') { + platformOwner = true; } - if (role.name == "platform-viewer") { - platformViewer = true + if (role.name === 'platform-viewer') { + platformViewer = true; } } // grab the users project ids and roles in the first request groupRoleProjectIds = await User.User(modelClients).getAllProjectsIdsForUser(currentUser.id, keycloakUsersGroups); await User.User(modelClients).userLastAccessed(currentUser); } catch (e) { - logger.error('Error loading user details', e.message ); + logger.error('Error loading user details', e.message); } } if (legacyGrant) { const { role } = legacyGrant; - if (role == 'admin') { - platformOwner = true + if (role === 'admin') { + platformOwner = true; } } @@ -180,8 +179,8 @@ async function initCheck() { // the viewAll permission check, to then error out and follow through with the standard user permission check, effectively costing 2 hasPermission calls for every request // this eliminates a huge number of these by making it available in the apollo context const hasPermission = req.kauth - ? keycloakHasPermission(req.kauth.grant, modelClients, serviceAccount, currentUser, groupRoleProjectIds) - : legacyHasPermission(req.legacyCredentials) + ? keycloakHasPermission(req.kauth.grant, modelClients, serviceAccount, currentUser, groupRoleProjectIds) + : legacyHasPermission(req.legacyCredentials); return { keycloakAdminClient, @@ -190,41 +189,41 @@ async function initCheck() { keycloakGrant, legacyGrant, userActivityLogger: (message, meta) => { - let defaultMeta = { + const defaultMeta = { user: req.kauth ? req.kauth.grant : req.legacyCredentials - ? req.legacyCredentials - : null, - headers: req.headers + ? req.legacyCredentials + : null, + headers: req.headers, }; return userActivityLogger.user_action(message, { ...defaultMeta, - ...meta + ...meta, }); }, models: { UserModel: User.User(modelClients), GroupModel: Group.Group(modelClients), - EnvironmentModel: Environment.Environment(modelClients) + EnvironmentModel: Environment.Environment(modelClients), }, keycloakUsersGroups, adminScopes: { - platformOwner: platformOwner, - platformViewer: platformViewer, + platformOwner, + platformViewer, }, }; } }, formatError: error => { - logger.error('GraphQL field error `' + error.path + '`: ' + error.message); + logger.error(`GraphQL field error \`${error.path}\`: ${error.message}`); return { message: error.message, locations: error.locations, path: error.path, ...(getConfigFromEnv('NODE_ENV') === 'development' ? { extensions: error.extensions } - : {}) + : {}), }; }, plugins: [ @@ -235,24 +234,21 @@ async function initCheck() { const queryString = R.prop('query', request); const variables = R.prop('variables', request); - const queryObject = gql` - ${queryString} - `; - const rootFieldName = queryObject.definitions[0].selectionSet.selections.reduce( - (init, q, idx) => - idx === 0 ? `${q.name.value}` : `${init}, ${q.name.value}`, - '' - ); + const queryObject = gqlParse(queryString); + const rootFieldName = queryObject.definitions[0].selectionSet.selections.reduce( + (init, q, idx) => idx === 0 ? `${q.name.value}` : `${init}, ${q.name.value}`, + '', + ); - // operationName is set by the client and optional. rootFieldName is - // set by the API type defs. - // operationName would be "getLagoonDemoProjectId" and rootFieldName - // would be "getProjectByName" with a query like: - // query getLagoonDemoProjectId { getProjectByName(name: "lagoon-demo") { id } } - const transactionName = operationName ? operationName : rootFieldName; - newrelic.setTransactionName(`graphql (${transactionName})`); - newrelic.addCustomAttribute('gqlQuery', queryString); - newrelic.addCustomAttribute('gqlVars', JSON.stringify(variables)); + // operationName is set by the client and optional. rootFieldName is + // set by the API type defs. + // operationName would be "getLagoonDemoProjectId" and rootFieldName + // would be "getProjectByName" with a query like: + // query getLagoonDemoProjectId { getProjectByName(name: "lagoon-demo") { id } } + const transactionName = operationName || rootFieldName; + newrelic.setTransactionName(`graphql (${transactionName})`); + newrelic.addCustomAttribute('gqlQuery', queryString); + newrelic.addCustomAttribute('gqlVars', JSON.stringify(variables)); return { willSendResponse: data => { @@ -261,14 +257,14 @@ async function initCheck() { 'errorCount', R.pipe( R.propOr([], 'errors'), - R.length - )(response) + R.length, + )(response), ); - } + }, }; - } - } - ] + }, + }, + ], }); } diff --git a/services/api/src/app.ts b/services/api/src/app.ts index a361c40aa6..43d857d0ee 100644 --- a/services/api/src/app.ts +++ b/services/api/src/app.ts @@ -20,21 +20,21 @@ app.use(json()); // Add custom configured logger (morgan through winston). app.use( morgan('combined', { - skip: (req, res) => { + skip: (req, _res) => { if (req.originalUrl.startsWith('/status')) { - return req.originalUrl.startsWith('/status') + return req.originalUrl.startsWith('/status'); } if (req.originalUrl.startsWith('/favicon.ico')) { - return req.originalUrl.startsWith('/favicon.ico') + return req.originalUrl.startsWith('/favicon.ico'); } if (req.originalUrl.startsWith('/.well-known')) { - return req.originalUrl.startsWith('/.well-known') + return req.originalUrl.startsWith('/.well-known'); } }, stream: { - write: message => logger.info(message.trim()) - } - }) + write: message => logger.info(message.trim()), + }, + }), ); // TODO: Restrict requests to lagoon domains? @@ -42,7 +42,7 @@ app.use(cors({ origin: process.env.CORS_ORIGIN || '*', credentials: true, methods: ['GET', 'POST', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'apollo-require-preflight'] + allowedHeaders: ['Content-Type', 'Authorization', 'apollo-require-preflight'], })); app.use(requestMiddleware); @@ -55,10 +55,10 @@ app.use('/', createRouter()); export async function configureApp() { async function setupGraphQLUpload() { try { - const { default: graphqlUploadExpress } = await import("graphql-upload/graphqlUploadExpress.mjs"); + const { default: graphqlUploadExpress } = await import('graphql-upload/graphqlUploadExpress.mjs'); app.use(graphqlUploadExpress({}) as unknown as express.RequestHandler); } catch (error) { - logger.error("Failed to load or setup graphql-upload:", error); + logger.error('Failed to load or setup graphql-upload:', error); throw error; } } @@ -69,11 +69,10 @@ export async function configureApp() { await apolloServer.start(); apolloServer.applyMiddleware({ app, - cors: false + cors: false, }); } catch (error) { - logger.error("Failed to start or apply Apollo Server middleware:", error); + logger.error('Failed to start or apply Apollo Server middleware:', error); throw error; } - } diff --git a/services/api/src/authMiddleware.ts b/services/api/src/authMiddleware.ts index 79d486e1b0..5581bf45e7 100644 --- a/services/api/src/authMiddleware.ts +++ b/services/api/src/authMiddleware.ts @@ -5,10 +5,11 @@ import { isLegacyToken, isKeycloakToken, getGrantForKeycloakToken, - getCredentialsForLegacyToken + getCredentialsForLegacyToken, } from './util/auth'; import { userActivityLogger } from './loggers/userActivityLogger'; -const { getClientIp } = require('@supercharge/request-ip'); + +const { _getClientIp } = require('@supercharge/request-ip'); export type RequestWithAuthData = Request & { legacyCredentials: any; @@ -21,24 +22,24 @@ const getBearerTokenFromHeader = R.compose( R.ifElse( splits => // @ts-ignore - R.length(splits) === 2 && - R.compose( + R.length(splits) === 2 + && R.compose( R.toLower, R.defaultTo(''), - R.head + R.head, // @ts-ignore )(splits) === 'bearer', R.nth(1), - R.always(null) + R.always(null), ), R.split(' '), - R.defaultTo('') + R.defaultTo(''), ); const authenticateJWT = async ( req: RequestWithAuthData, res: Response, - next: NextFunction + next: NextFunction, ) => { // Allow access to status without auth. if (req.url === '/status') { @@ -63,7 +64,7 @@ const authenticateJWT = async ( const decodedToken = decode(token, { json: true, complete: true }); if (decodedToken === null) { res.status(401).send({ - errors: [{ message: 'Unauthorized - Bearer token malformed' }] + errors: [{ message: 'Unauthorized - Bearer token malformed' }], }); return; } @@ -76,24 +77,23 @@ const authenticateJWT = async ( req.legacyCredentials = legacyCredentials; const { sub, iss } = legacyCredentials; - const username = sub ? sub : 'unknown'; - const source = iss ? iss : 'unknown'; + const username = sub || 'unknown'; + const source = iss || 'unknown'; userActivityLogger.user_auth( `Legacy authentication granted for '${username}' from '${source}'`, { - user: legacyCredentials ? legacyCredentials : null, - headers: req.headers - } + user: legacyCredentials || null, + headers: req.headers, + }, ); return next(); } catch (e) { res.status(401).send({ errors: [ - { message: `Unauthorized - Legacy token invalid: ${e.message}` } - ] + { message: `Unauthorized - Legacy token invalid: ${e.message}` }, + ], }); - return; } } else if (isKeycloakToken(decodedToken)) { try { @@ -103,34 +103,32 @@ const authenticateJWT = async ( const { azp: source, preferred_username, - email + email, } = grant.access_token.content; - const username = preferred_username ? preferred_username : 'unknown'; + const username = preferred_username || 'unknown'; userActivityLogger.user_auth( `Keycloak authentication granted for '${username} (${ - email ? email : 'unknown' + email || 'unknown' })' from '${source}'`, { user: grant ? grant.access_token.content : null, - headers: req.headers - } + headers: req.headers, + }, ); return next(); } catch (e) { res.status(401).send({ errors: [ - { message: `Unauthorized - Keycloak token invalid: ${e.message}` } - ] + { message: `Unauthorized - Keycloak token invalid: ${e.message}` }, + ], }); - return; } } else { res.status(401).send({ - errors: [{ message: `Unauthorized - Bearer token unrecognized` }] + errors: [{ message: 'Unauthorized - Bearer token unrecognized' }], }); - return; } }; diff --git a/services/api/src/clients/aws.ts b/services/api/src/clients/aws.ts index 015c18cae6..9d7b0587b0 100644 --- a/services/api/src/clients/aws.ts +++ b/services/api/src/clients/aws.ts @@ -1,12 +1,13 @@ -const { S3Client } = require('@aws-sdk/client-s3'); import { getConfigFromEnv } from '../util/config'; +const { S3Client } = require('@aws-sdk/client-s3'); + export const config = { origin: getConfigFromEnv('S3_FILES_HOST', 'http://docker.for.mac.localhost:9000'), accessKeyId: getConfigFromEnv('S3_FILES_ACCESS_KEY_ID', 'minio'), secretAccessKey: getConfigFromEnv('S3_FILES_SECRET_ACCESS_KEY', 'minio123'), region: getConfigFromEnv('S3_FILES_REGION', 'us-east-1'), - bucket: getConfigFromEnv('S3_FILES_BUCKET', 'lagoon-files') + bucket: getConfigFromEnv('S3_FILES_BUCKET', 'lagoon-files'), }; export const s3Client = new S3Client({ @@ -17,5 +18,5 @@ export const s3Client = new S3Client({ }, region: config.region, forcePathStyle: true, - signatureVersion: 'v4' -}); \ No newline at end of file + signatureVersion: 'v4', +}); diff --git a/services/api/src/clients/esClient.ts b/services/api/src/clients/esClient.ts index 706f1e6775..5edb65ac2a 100644 --- a/services/api/src/clients/esClient.ts +++ b/services/api/src/clients/esClient.ts @@ -4,11 +4,11 @@ import { getConfigFromEnv } from '../util/config'; export const config = { host: getConfigFromEnv('ELASTICSEARCH_URL', 'http://logs-db-service:9200'), user: 'admin', - pass: getConfigFromEnv('LOGSDB_ADMIN_PASSWORD', '') + pass: getConfigFromEnv('LOGSDB_ADMIN_PASSWORD', ''), }; export const esClient = new Client({ host: config.host, httpAuth: `${config.user}:${config.pass}`, - log: 'warning' + log: 'warning', }); diff --git a/services/api/src/clients/keycloak-admin.ts b/services/api/src/clients/keycloak-admin.ts index 80a03e71a3..8000094e70 100644 --- a/services/api/src/clients/keycloak-admin.ts +++ b/services/api/src/clients/keycloak-admin.ts @@ -18,8 +18,8 @@ export const isNetworkError = ( } return ( - 'response' in error && - typeof error.response === 'object' + 'response' in error + && typeof error.response === 'object' ); }; @@ -34,7 +34,6 @@ export const getKeycloakAdminClient = async (): Promise => */ keycloakAdminClient.registerTokenProvider({ async getAccessToken() { - if (keycloakAdminClient.accessToken) { const token = decode(keycloakAdminClient.accessToken, { json: true }); const now = Math.floor(Date.now() / 1000); @@ -57,7 +56,7 @@ export const getKeycloakAdminClient = async (): Promise => clientId: 'admin-api', clientSecret: getConfigFromEnv( 'KEYCLOAK_ADMIN_API_CLIENT_SECRET', - '' + '', ), }); diff --git a/services/api/src/clients/keycloakClient.ts b/services/api/src/clients/keycloakClient.ts index 331c5fb1c5..e5d1d3f830 100644 --- a/services/api/src/clients/keycloakClient.ts +++ b/services/api/src/clients/keycloakClient.ts @@ -8,11 +8,11 @@ export const config = { realm: 'lagoon', apiClientSecret: getConfigFromEnv( 'KEYCLOAK_API_CLIENT_SECRET', - '' + '', ), get publicRoute() { return getLagoonRouteFromEnv(/keycloak-/, this.origin); - } + }, }; const keycloakConfig = new KeycloakConfig({ @@ -21,8 +21,8 @@ const keycloakConfig = new KeycloakConfig({ clientId: 'api', bearerOnly: true, credentials: { - secret: config.apiClientSecret - } + secret: config.apiClientSecret, + }, }); export const keycloakGrantManager = new KeycloakGrantManager(keycloakConfig); @@ -32,7 +32,7 @@ export const keycloakGrantManager = new KeycloakGrantManager(keycloakConfig); // the URL used in the UI doesn't match what's used in the API. keycloakGrantManager.validateToken = function validateToken( token, - expectedType + expectedType, ) { return new Promise((resolve, reject) => { if (!token) { @@ -48,17 +48,17 @@ keycloakGrantManager.validateToken = function validateToken( } else if (!token.content.iss.includes('auth/realms/lagoon')) { reject(new Error('invalid token (wrong ISS)')); } else { - const audienceData = Array.isArray(token.content.aud) ? token.content.aud : [token.content.aud] + const audienceData = Array.isArray(token.content.aud) ? token.content.aud : [token.content.aud]; if (expectedType === 'ID') { if (!audienceData.includes(this.clientId)) { - reject(new Error('invalid token (wrong audience)')) + reject(new Error('invalid token (wrong audience)')); } if (token.content.azp && token.content.azp !== this.clientId) { - reject(new Error('invalid token (authorized party should match client id)')) + reject(new Error('invalid token (authorized party should match client id)')); } } else if (this.verifyTokenAudience) { if (!audienceData.includes(this.clientId)) { - reject(new Error('invalid token (wrong audience)')) + reject(new Error('invalid token (wrong audience)')); } } const verify = crypto.createVerify('RSA-SHA256'); @@ -71,11 +71,11 @@ keycloakGrantManager.validateToken = function validateToken( } else { resolve(token); } - } catch (err) { + } catch (_err) { reject( new Error( - 'Misconfigured parameters while validating token. Check your keycloak.json file!' - ) + 'Misconfigured parameters while validating token. Check your keycloak.json file!', + ), ); } } else { @@ -91,7 +91,7 @@ keycloakGrantManager.validateToken = function validateToken( } }) .catch(err => { - reject(new Error('failed to load public key to verify token. Reason: ' + err.message)); + reject(new Error(`failed to load public key to verify token. Reason: ${err.message}`)); }); } } diff --git a/services/api/src/clients/pubSub.ts b/services/api/src/clients/pubSub.ts index 4e028ba0c3..9ac458724c 100644 --- a/services/api/src/clients/pubSub.ts +++ b/services/api/src/clients/pubSub.ts @@ -1,7 +1,6 @@ import { path } from 'ramda'; import { withFilter } from 'graphql-subscriptions'; import { RedisPubSub } from 'graphql-redis-subscriptions'; -import { ForbiddenError } from 'apollo-server-express'; import { query } from '../util/db'; import { Sql as environmentSql } from '../resources/environment/sql'; import { ResolverFn } from '../resources'; @@ -10,20 +9,20 @@ import { config } from './redisClient'; export const EVENTS = { DEPLOYMENT: 'api.subscription.deployment', BACKUP: 'api.subscription.backup', - TASK: 'api.subscription.task' + TASK: 'api.subscription.task', }; const endpoint = `redis://:${config.pass}@${config.hostname}:${config.port}`; export const pubSub = new RedisPubSub({ - connection: endpoint + connection: endpoint, }); const createSubscribe = (events): ResolverFn => async ( rootValue, args, context, - info + info, ) => { const filtered = withFilter( () => pubSub.asyncIterator(events), @@ -46,7 +45,7 @@ const createSubscribe = (events): ResolverFn => async ( // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project + project, }); } if (!payload || payload.environment === undefined) { @@ -66,5 +65,5 @@ const createSubscribe = (events): ResolverFn => async ( export const createEnvironmentFilteredSubscriber = events => ({ resolve: payload => payload, - subscribe: createSubscribe(events) + subscribe: createSubscribe(events), }); diff --git a/services/api/src/clients/redisClient.ts b/services/api/src/clients/redisClient.ts index c8e47a4790..58a5292798 100644 --- a/services/api/src/clients/redisClient.ts +++ b/services/api/src/clients/redisClient.ts @@ -36,7 +36,7 @@ export const redisClient = redis.createClient({ enable_offline_queue: false, }); -redisClient.on('error', function (error) { +redisClient.on('error', (error) => { console.error(error); }); diff --git a/node-packages/commons/src/__tests__/__snapshots__/jwt.test.ts.snap b/services/api/src/commons/__tests__/__snapshots__/jwt.test.ts.snap similarity index 100% rename from node-packages/commons/src/__tests__/__snapshots__/jwt.test.ts.snap rename to services/api/src/commons/__tests__/__snapshots__/jwt.test.ts.snap diff --git a/node-packages/commons/src/__tests__/jwt.test.ts b/services/api/src/commons/__tests__/jwt.test.ts similarity index 97% rename from node-packages/commons/src/__tests__/jwt.test.ts rename to services/api/src/commons/__tests__/jwt.test.ts index aa17981b6f..ae53bf5791 100644 --- a/node-packages/commons/src/__tests__/jwt.test.ts +++ b/services/api/src/commons/__tests__/jwt.test.ts @@ -8,7 +8,7 @@ describe('createJWT', () => { role: 'admin' as const, iat: 1503014400000, exp: 1503014400000, - iss: 'jest' + iss: 'jest', }, jwtSecret: 'secret', }; @@ -47,7 +47,7 @@ describe('createJWT', () => { userId: 0, permissions: { projects: [], - customers: [] + customers: [], }, role: 'admin' as const, iat: 1503014400, // way back in the past diff --git a/node-packages/commons/src/api.ts b/services/api/src/commons/api.ts similarity index 84% rename from node-packages/commons/src/api.ts rename to services/api/src/commons/api.ts index 97f9ee437b..31f5fe9fb3 100644 --- a/node-packages/commons/src/api.ts +++ b/services/api/src/commons/api.ts @@ -1,6 +1,6 @@ import { Lokka } from 'lokka'; -import { Transport } from './lokka-transport-http-retry'; import { replace, pipe, toLower } from 'ramda'; +import { Transport } from './lokka-transport-http-retry'; import { getConfigFromEnv } from './util/config'; import { TaskStatusType, TaskSourceType } from './types'; @@ -126,7 +126,7 @@ interface RestorePatch { restoreLocation?: string; } -enum EnvType { +enum _EnvType { PRODUCTION = 'production', DEVELOPMENT = 'development' } @@ -144,17 +144,17 @@ export enum EnvVariableScope { CONTAINER_REGISTRY = 'container_registry', } -let transportOptions: { +const transportOptions: { headers: { Authorization?: string }, timeout: number } = { headers: {}, - timeout: 60000 + timeout: 60000, }; -const transport = new Transport(`${getConfigFromEnv('API_HOST', 'http://api:3000')}/graphql`, {transportOptions}); +const transport = new Transport(`${getConfigFromEnv('API_HOST', 'http://api:3000')}/graphql`, { transportOptions }); export const graphqlapi = new Lokka({ transport }); export const lagoonApi = graphqlapi; @@ -232,9 +232,8 @@ fragment on Backup { } `); -export const addGroup = (name: string): Promise => - graphqlapi.mutate( - ` +export const addGroup = (name: string): Promise => graphqlapi.mutate( + ` ($name: String!) { addGroup(input: { name: $name @@ -243,17 +242,16 @@ export const addGroup = (name: string): Promise => } } `, - { - name - } - ); + { + name, + }, +); export const addGroupWithParent = ( name: string, - parentGroupName: string -): Promise => - graphqlapi.mutate( - ` + parentGroupName: string, +): Promise => graphqlapi.mutate( + ` ($name: String!, $parentGroupName: String) { addGroup(input: { name: $name @@ -263,21 +261,20 @@ export const addGroupWithParent = ( } } `, - { - name, - parentGroupName - } - ); + { + name, + parentGroupName, + }, +); export const addBackup = ( id: number | null = null, environment: number, source: string, backupId: string, - created: string -): Promise => - graphqlapi.mutate( - ` + created: string, +): Promise => graphqlapi.mutate( + ` ($id: Int, $environment: Int!, $source: String!, $backupId: String!, $created: String!) { addBackup(input: { id: $id @@ -290,26 +287,25 @@ export const addBackup = ( } } `, - { - id, - environment, - source, - backupId, - created - } - ); + { + id, + environment, + source, + backupId, + created, + }, +); -export const deleteBackup = (backupId: string): Promise => - graphqlapi.mutate( - ` +export const deleteBackup = (backupId: string): Promise => graphqlapi.mutate( + ` ($backupId: String!) { deleteBackup(input: { backupId: $backupId }) } `, - { backupId } - ); + { backupId }, +); const restoreFragment = graphqlapi.createFragment(` fragment on Restore { @@ -323,10 +319,9 @@ const restoreFragment = graphqlapi.createFragment(` export const updateRestore = ( backupId: string, - patch: RestorePatch -): Promise => - graphqlapi.mutate( - ` + patch: RestorePatch, +): Promise => graphqlapi.mutate( + ` ($backupId: String!, $patch: UpdateRestorePatchInput!) { updateRestore(input: { backupId: $backupId @@ -336,14 +331,13 @@ export const updateRestore = ( } } `, - { backupId, patch } - ); + { backupId, patch }, +); export const getEnvironmentBackups = ( - openshiftProjectName: string -): Promise => - graphqlapi.query( - ` + openshiftProjectName: string, +): Promise => graphqlapi.query( + ` query environmentByOpenshiftProjectName($openshiftProjectName: String!) { environmentByOpenshiftProjectName(openshiftProjectName: $openshiftProjectName) { id @@ -361,12 +355,11 @@ export const getEnvironmentBackups = ( } } `, - { openshiftProjectName } - ); + { openshiftProjectName }, +); -export const updateGroup = (name: string, patch: GroupPatch): Promise => - graphqlapi.mutate( - ` +export const updateGroup = (name: string, patch: GroupPatch): Promise => graphqlapi.mutate( + ` ($name: String!, $patch: UpdateGroupPatchInput!) { updateGroup(input: { group: { @@ -378,12 +371,11 @@ export const updateGroup = (name: string, patch: GroupPatch): Promise => } } `, - { name, patch } - ); + { name, patch }, +); -export const deleteGroup = (name: string): Promise => - graphqlapi.mutate( - ` +export const deleteGroup = (name: string): Promise => graphqlapi.mutate( + ` ($name: String!) { deleteGroup(input: { group: { @@ -392,30 +384,28 @@ export const deleteGroup = (name: string): Promise => }) } `, - { name } - ); + { name }, +); -export const getUserBySshKey = (sshKey: string): Promise => - graphqlapi.query( - ` +export const getUserBySshKey = (sshKey: string): Promise => graphqlapi.query( + ` query userBySshKey($sshKey: String!) { userBySshKey(sshKey: $sshKey) { ...${userFragment} } } `, - { sshKey } - ); + { sshKey }, +); export const addUser = ( email: string, firstName: string | null = null, lastName: string | null = null, comment: string | null = null, - gitlabId: number | null = null -): Promise => - graphqlapi.mutate( - ` + gitlabId: number | null = null, +): Promise => graphqlapi.mutate( + ` ($email: String!, $firstName: String, $lastName: String, $comment: String, $gitlabId: Int) { addUser(input: { email: $email @@ -428,18 +418,17 @@ export const addUser = ( } } `, - { - email, - firstName, - lastName, - comment, - gitlabId - } - ); + { + email, + firstName, + lastName, + comment, + gitlabId, + }, +); -export const updateUser = (email: string, patch: UserPatch): Promise => - graphqlapi.mutate( - ` +export const updateUser = (email: string, patch: UserPatch): Promise => graphqlapi.mutate( + ` ($email: String!, $patch: UpdateUserPatchInput!) { updateUser(input: { user: { @@ -451,12 +440,11 @@ export const updateUser = (email: string, patch: UserPatch): Promise => } } `, - { email, patch } - ); + { email, patch }, +); -export const deleteUser = (email: string): Promise => - graphqlapi.mutate( - ` +export const deleteUser = (email: string): Promise => graphqlapi.mutate( + ` ($email: String!) { deleteUser(input: { user: { @@ -465,16 +453,15 @@ export const deleteUser = (email: string): Promise => }) } `, - { email } - ); + { email }, +); export const addUserToGroup = ( userEmail: string, groupName: string, - role: string -): Promise => - graphqlapi.mutate( - ` + role: string, +): Promise => graphqlapi.mutate( + ` ($userEmail: String!, $groupName: String!, $role: GroupRole!) { addUserToGroup(input: { user: { email: $userEmail } @@ -485,15 +472,14 @@ export const addUserToGroup = ( } } `, - { userEmail, groupName, role } - ); + { userEmail, groupName, role }, +); export const addGroupToProject = ( project: string, - group: string -): Promise => - graphqlapi.mutate( - ` + group: string, +): Promise => graphqlapi.mutate( + ` ($project: String!, $group: String!) { addGroupsToProject(input: { project: { name: $project} @@ -503,15 +489,14 @@ export const addGroupToProject = ( } } `, - { project, group } - ); + { project, group }, +); export const removeGroupFromProject = ( project: string, - group: string -): Promise => - graphqlapi.mutate( - ` + group: string, +): Promise => graphqlapi.mutate( + ` ($project: String!, $group: String!) { removeGroupsFromProject(input: { project: { name: $project} @@ -521,15 +506,14 @@ export const removeGroupFromProject = ( } } `, - { project, group } - ); + { project, group }, +); export const removeUserFromGroup = ( userEmail: string, - groupName: string -): Promise => - graphqlapi.mutate( - ` + groupName: string, +): Promise => graphqlapi.mutate( + ` ($userEmail: String!, $groupName: String!) { removeUserFromGroup(input: { user: { email: $userEmail } @@ -539,18 +523,17 @@ export const removeUserFromGroup = ( } } `, - { userEmail, groupName } - ); + { userEmail, groupName }, +); export const addSshKey = ( id: number | null = null, name: string, keyValue: string, keyType: string, - userEmail: string -): Promise => - graphqlapi.mutate( - ` + userEmail: string, +): Promise => graphqlapi.mutate( + ` ($id: Int, $name: String!, $keyValue: String!, $keyType: SshKeyType!, $userEmail: String!) { addSshKey(input: { id: $id @@ -565,38 +548,36 @@ export const addSshKey = ( } } `, - { - id, - name, - keyValue, - userEmail, - keyType - } - ); + { + id, + name, + keyValue, + userEmail, + keyType, + }, +); -export const deleteSshKey = (name: string): Promise => - graphqlapi.mutate( - ` +export const deleteSshKey = (name: string): Promise => graphqlapi.mutate( + ` ($name: String!) { deleteSshKey(input: { name: $name }) } `, - { - name, - }, - ); + { + name, + }, +); export const addProject = ( name: string, gitUrl: string, openshift: number, productionenvironment: string, - id: number | null = null -): Promise => - graphqlapi.mutate( - ` + id: number | null = null, +): Promise => graphqlapi.mutate( + ` ($name: String!, $gitUrl: String!, $openshift: Int!, $productionenvironment: String!, $id: Int) { addProject(input: { name: $name, @@ -609,21 +590,20 @@ export const addProject = ( } } `, - { - name, - gitUrl, - openshift, - productionenvironment, - id - } - ); + { + name, + gitUrl, + openshift, + productionenvironment, + id, + }, +); export const updateProject = ( id: number, - patch: ProjectPatch -): Promise => - graphqlapi.mutate( - ` + patch: ProjectPatch, +): Promise => graphqlapi.mutate( + ` ($id: Int!, $patch: UpdateProjectPatchInput!) { updateProject(input: { id: $id @@ -633,20 +613,19 @@ export const updateProject = ( } } `, - { id, patch } - ); + { id, patch }, +); -export const deleteProject = (name: string): Promise => - graphqlapi.mutate( - ` +export const deleteProject = (name: string): Promise => graphqlapi.mutate( + ` ($name: String!) { deleteProject(input: { project: $name }) } `, - { name } - ); + { name }, +); export async function getProjectByName(project: string): Promise { const result = await graphqlapi.query(` @@ -667,20 +646,18 @@ export async function getProjectByName(project: string): Promise { export const allProjectsInGroup = (groupInput: { id?: string; name?: string; -}): Promise => - graphqlapi.query( - ` +}): Promise => graphqlapi.query( + ` query($groupInput: GroupInput!) { allProjectsInGroup(input: $groupInput) { ...${projectFragment} } } `, - { - groupInput - } - ); - + { + groupInput, + }, +); const apiRouteFragment = graphqlapi.createFragment(` fragment on Route { @@ -727,7 +704,7 @@ fragment on AutogeneratedRouteConfig { `); export async function getEnvironmentByIdWithVariables( - id: number + id: number, ): Promise { const result = await graphqlapi.query(` { @@ -758,7 +735,7 @@ export async function getEnvironmentByIdWithVariables( if (!result || !result.environmentById) { throw new EnvironmentNotFound( - `Cannot find environment for id ${id}\n${result.environmentById}` + `Cannot find environment for id ${id}\n${result.environmentById}`, ); } @@ -766,7 +743,7 @@ export async function getEnvironmentByIdWithVariables( } export async function getEnvironmentByOpenshiftProjectName( - openshiftProjectName: string + openshiftProjectName: string, ): Promise { const result = await graphqlapi.query(` { @@ -782,7 +759,7 @@ export async function getEnvironmentByOpenshiftProjectName( if (!result || !result.environmentByOpenshiftProjectName) { throw new EnvironmentNotFound( - `Cannot find environment for OpenshiftProjectName ${openshiftProjectName}\n${result.environmentByOpenshiftProjectName}` + `Cannot find environment for OpenshiftProjectName ${openshiftProjectName}\n${result.environmentByOpenshiftProjectName}`, ); } @@ -821,8 +798,7 @@ interface GetOpenshiftInfoForProjectResult { }; } -export const getOpenShiftInfoForProject = (project: string): Promise => - graphqlapi.query(` +export const getOpenShiftInfoForProject = (project: string): Promise => graphqlapi.query(` { project:projectByName(name: "${project}"){ autoIdle @@ -864,8 +840,7 @@ export const getOpenShiftInfoForProject = (project: string): Promise => - graphqlapi.query(` +export const getDeployTargetConfigsForProject = (project: number): Promise => graphqlapi.query(` { targets:deployTargetConfigsByProjectId(project: ${project}){ id @@ -893,8 +868,7 @@ export interface GetOpenShiftInfoForEnvironmentResult { environment: GetOpenShiftInfoForEnvironmentEnvironmentResult } -export const getOpenShiftInfoForEnvironment = (environment: number): Promise => - graphqlapi.query(` +export const getOpenShiftInfoForEnvironment = (environment: number): Promise => graphqlapi.query(` { environment:environmentById(id: ${environment}){ id @@ -981,9 +955,8 @@ export const addTask = ( execute: boolean = false, sourceUser: string | null = null, sourceType: TaskSourceType, -) => - graphqlapi.mutate( - ` +) => graphqlapi.mutate( + ` ($name: String!, $status: TaskStatusType!, $created: String!, $environment: Int!, $id: Int, $remoteId: String, $started: String, $completed: String, $service: String, $command: String, $execute: Boolean, $sourceUser: String, $sourceType: TaskSourceType) { @@ -1006,22 +979,22 @@ export const addTask = ( } } `, - { - name, - status: status.toUpperCase(), - created, - environment, - id, - remoteId, - started, - completed, - service, - command, - execute, - sourceUser, - sourceType: sourceType.toUpperCase(), - }, - ); + { + name, + status: status.toUpperCase(), + created, + environment, + id, + remoteId, + started, + completed, + service, + command, + execute, + sourceUser, + sourceType: sourceType.toUpperCase(), + }, +); const taskFragment = graphqlapi.createFragment(` fragment on Task { @@ -1040,9 +1013,9 @@ fragment on Task { export const sanitizeGroupName = pipe( replace(/[^a-zA-Z0-9-]/g, '-'), - toLower + toLower, ); export const sanitizeProjectName = pipe( replace(/[^a-zA-Z0-9-]/g, '-'), - toLower + toLower, ); diff --git a/node-packages/commons/src/gitlab/api.ts b/services/api/src/commons/gitlab/api.ts similarity index 77% rename from node-packages/commons/src/gitlab/api.ts rename to services/api/src/commons/gitlab/api.ts index 41ab72145f..6b073fbcd8 100644 --- a/node-packages/commons/src/gitlab/api.ts +++ b/services/api/src/commons/gitlab/api.ts @@ -9,8 +9,8 @@ const options = { baseURL: `${API_HOST}/api/v4/`, timeout: 30000, headers: { - 'Private-Token': API_TOKEN - } + 'Private-Token': API_TOKEN, + }, }; const gitlabapi = axios.create(options); @@ -57,10 +57,9 @@ const getRequest = async (url: string): Promise => { if (axios.isAxiosError(error)) { if (error.response) { const errorMessage = error.response?.data?.message ?? error.message; - const errorString = - typeof errorMessage === 'string' - ? errorMessage - : JSON.stringify(errorMessage); + const errorString = typeof errorMessage === 'string' + ? errorMessage + : JSON.stringify(errorMessage); throw new APIError(errorString); } else if (error.request) { @@ -82,10 +81,9 @@ const postRequest = async (url: string, body: any): Promise => { if (axios.isAxiosError(error)) { if (error.response) { const errorMessage = error.response?.data?.message ?? error.message; - const errorString = - typeof errorMessage === 'string' - ? errorMessage - : JSON.stringify(errorMessage); + const errorString = typeof errorMessage === 'string' + ? errorMessage + : JSON.stringify(errorMessage); throw new APIError(errorString); } else if (error.request) { @@ -109,8 +107,8 @@ const getAllPagesRequest = async (url: string): Promise => { const response = await gitlabapi.get(url, { params: { per_page: 100, - page - } + page, + }, }); if (response.data.length === 0) { @@ -123,10 +121,9 @@ const getAllPagesRequest = async (url: string): Promise => { if (axios.isAxiosError(error)) { if (error.response) { const errorMessage = error.response?.data?.message ?? error.message; - const errorString = - typeof errorMessage === 'string' - ? errorMessage - : JSON.stringify(errorMessage); + const errorString = typeof errorMessage === 'string' + ? errorMessage + : JSON.stringify(errorMessage); throw new APIError(errorString); } else if (error.request) { @@ -147,8 +144,8 @@ export const getUserByUsername = async (username: string): Promise => { try { const response = await gitlabapi.get('users', { params: { - username - } + username, + }, }); if (response.data.length === 0) { @@ -160,10 +157,9 @@ export const getUserByUsername = async (username: string): Promise => { if (axios.isAxiosError(error)) { if (error.response) { const errorMessage = error.response?.data?.message ?? error.message; - const errorString = - typeof errorMessage === 'string' - ? errorMessage - : JSON.stringify(errorMessage); + const errorString = typeof errorMessage === 'string' + ? errorMessage + : JSON.stringify(errorMessage); throw new APIError(errorString); } else if (error.request) { @@ -178,28 +174,20 @@ export const getUserByUsername = async (username: string): Promise => { }; export const getAllGroups = async () => getAllPagesRequest('groups'); -export const getGroup = async (groupId: number): Promise => - getRequest(`groups/${groupId}`); -export const getGroupMembers = async (groupId: number): Promise => - getRequest(`groups/${groupId}/members`); -export const getAllProjects = async (): Promise => - getAllPagesRequest('projects'); -export const getProject = async (projectId: number): Promise => - getRequest(`projects/${projectId}`); -export const getProjectMembers = async (projectId: number): Promise => - getRequest(`projects/${projectId}/members`); +export const getGroup = async (groupId: number): Promise => getRequest(`groups/${groupId}`); +export const getGroupMembers = async (groupId: number): Promise => getRequest(`groups/${groupId}/members`); +export const getAllProjects = async (): Promise => getAllPagesRequest('projects'); +export const getProject = async (projectId: number): Promise => getRequest(`projects/${projectId}`); +export const getProjectMembers = async (projectId: number): Promise => getRequest(`projects/${projectId}/members`); export const getAllUsers = async () => getAllPagesRequest('users'); -export const getUser = async (userId: number): Promise => - getRequest(`users/${userId}`); -export const getSshKey = async (keyId: number): Promise => - getRequest(`keys/${keyId}`); +export const getUser = async (userId: number): Promise => getRequest(`users/${userId}`); +export const getSshKey = async (keyId: number): Promise => getRequest(`keys/${keyId}`); export const addDeployKeyToProject = async ( projectId: number, - key: string -): Promise => - postRequest(`projects/${projectId}/deploy_keys`, { - title: 'Lagoon Project Key', - key, - can_push: false - }); + key: string, +): Promise => postRequest(`projects/${projectId}/deploy_keys`, { + title: 'Lagoon Project Key', + key, + can_push: false, +}); diff --git a/node-packages/commons/src/gitlab/types.ts b/services/api/src/commons/gitlab/types.ts similarity index 100% rename from node-packages/commons/src/gitlab/types.ts rename to services/api/src/commons/gitlab/types.ts diff --git a/node-packages/commons/src/jwt.ts b/services/api/src/commons/jwt.ts similarity index 99% rename from node-packages/commons/src/jwt.ts rename to services/api/src/commons/jwt.ts index 80b1cee815..f7b64e22dd 100644 --- a/node-packages/commons/src/jwt.ts +++ b/services/api/src/commons/jwt.ts @@ -1,4 +1,5 @@ import { sign } from 'jsonwebtoken'; + type Role = 'none' | 'admin' | 'drush'; interface Payload { diff --git a/node-packages/commons/src/logs/index.ts b/services/api/src/commons/logs/index.ts similarity index 88% rename from node-packages/commons/src/logs/index.ts rename to services/api/src/commons/logs/index.ts index 8117adbb84..bc773e13ab 100644 --- a/node-packages/commons/src/logs/index.ts +++ b/services/api/src/commons/logs/index.ts @@ -5,7 +5,7 @@ export const levels = { info: 3, verbose: 4, debug: 5, - trace: 6 + trace: 6, }; export const colors = { @@ -15,5 +15,5 @@ export const colors = { info: 'magenta', verbose: 'white', debug: 'gray', - trace: 'cyan' + trace: 'cyan', }; diff --git a/node-packages/commons/src/logs/lagoon-logger.ts b/services/api/src/commons/logs/lagoon-logger.ts similarity index 79% rename from node-packages/commons/src/logs/lagoon-logger.ts rename to services/api/src/commons/logs/lagoon-logger.ts index 039e55085e..7036841099 100644 --- a/node-packages/commons/src/logs/lagoon-logger.ts +++ b/services/api/src/commons/logs/lagoon-logger.ts @@ -1,8 +1,8 @@ import { has } from 'ramda'; import { ChannelWrapper, connect } from 'amqp-connection-manager'; -import { logger } from './local-logger'; -import { levels } from './'; import { ConfirmChannel } from 'amqplib'; +import { logger } from './local-logger'; +import { levels } from '.'; const rabbitmqHost = process.env.RABBITMQ_HOST || 'broker'; const rabbitmqUsername = process.env.RABBITMQ_USERNAME || 'guest'; @@ -14,29 +14,27 @@ export function initSendToLagoonLogs() { const connection = connect( [`amqp://${rabbitmqUsername}:${rabbitmqPassword}@${rabbitmqHost}`], // @ts-ignore - { json: true } + { json: true }, ); - connection.on('connect', ({ url }) => - logger.verbose('lagoon-logs: Connected to %s', url, { - action: 'connected', - url - }) + connection.on('connect', ({ url }) => logger.verbose('lagoon-logs: Connected to %s', url, { + action: 'connected', + url, + }), ); connection.on('disconnect', params => // @ts-ignore logger.error('lagoon-logs: Not connected, error: %s', params.err.code, { action: 'disconnected', - reason: params - }) + reason: params, + }), ); // Cast any to ChannelWrapper to get type-safetiness through our own code channelWrapperLogs = connection.createChannel({ - setup: (channel: ConfirmChannel) => - Promise.all([ - channel.assertExchange('lagoon-logs', 'direct', { durable: true }) - ]) + setup: (channel: ConfirmChannel) => Promise.all([ + channel.assertExchange('lagoon-logs', 'direct', { durable: true }), + ]), }); } @@ -47,7 +45,7 @@ export async function sendToLagoonLogs( event: string, meta: any, message: string, - level?: string + level?: string, ): Promise { const payload = { severity, @@ -56,7 +54,7 @@ export async function sendToLagoonLogs( event, meta, message, - level + level, }; try { @@ -64,7 +62,7 @@ export async function sendToLagoonLogs( const packageName = process.env.npm_package_name || ''; const options = { persistent: true, - appId: packageName + appId: packageName, }; await channelWrapperLogs.publish('lagoon-logs', '', buffer, options); @@ -73,7 +71,7 @@ export async function sendToLagoonLogs( } } catch (error) { logger.error( - `lagoon-logs: Error send to rabbitmq lagoon-logs exchange, error: ${error}` + `lagoon-logs: Error send to rabbitmq lagoon-logs exchange, error: ${error}`, ); } } diff --git a/node-packages/commons/src/logs/local-logger.ts b/services/api/src/commons/logs/local-logger.ts similarity index 88% rename from node-packages/commons/src/logs/local-logger.ts rename to services/api/src/commons/logs/local-logger.ts index 83d1b6bca4..e935f5730a 100644 --- a/node-packages/commons/src/logs/local-logger.ts +++ b/services/api/src/commons/logs/local-logger.ts @@ -1,6 +1,9 @@ -const { addColors, createLogger, format, transports } = require('winston'); import { getConfigFromEnv } from '../util/config'; -import { levels, colors } from './'; +import { levels, colors } from '.'; + +const { + addColors, createLogger, format, transports, +} = require('winston'); export interface LogFn { (...args: any[]): void; diff --git a/node-packages/commons/src/lokka-transport-http-retry.ts b/services/api/src/commons/lokka-transport-http-retry.ts similarity index 57% rename from node-packages/commons/src/lokka-transport-http-retry.ts rename to services/api/src/commons/lokka-transport-http-retry.ts index 6277ecd2d4..2a4b1e8188 100644 --- a/node-packages/commons/src/lokka-transport-http-retry.ts +++ b/services/api/src/commons/lokka-transport-http-retry.ts @@ -17,7 +17,7 @@ class ApiError extends Error {} const generateToken = (): string => { if (!envHasConfig('JWTSECRET') || !envHasConfig('JWTAUDIENCE')) { logger.error( - 'Unable to create api token due to missing `JWTSECRET`/`JWTAUDIENCE` environment variables' + 'Unable to create api token due to missing `JWTSECRET`/`JWTAUDIENCE` environment variables', ); return ''; } @@ -28,51 +28,51 @@ const generateToken = (): string => { iss: 'lagoon-internal', aud: getConfigFromEnv('JWTAUDIENCE'), // set a 60s expiry on the token - exp: Math.floor(Date.now() / 1000) + 60 + exp: Math.floor(Date.now() / 1000) + 60, }, - getConfigFromEnv('JWTSECRET') + getConfigFromEnv('JWTSECRET'), ); return `Bearer ${apiAdminToken}`; }; // Retries the fetch if operational/network errors occur -const retryFetch = (endpoint: string, options: any, retriesLeft = 5, interval = 1000): Promise => - new Promise((resolve, reject) => { - // get a fresh token for every request - options.headers.Authorization = generateToken(); +const retryFetch = (endpoint: string, options: any, retriesLeft = 5, interval = 1000): Promise => new Promise((resolve, reject) => { + // get a fresh token for every request + options.headers.Authorization = generateToken(); - return fetchUrl(endpoint, options) - .then(response => { - if (response.status !== 200 && response.status !== 400) { - throw new NetworkError(`Invalid status code: ${response.status}`); - } - return response.json(); - }) - .then(({ data, errors }) => { - if (errors) { - const error = new ApiError(`GraphQL Error: ${errors[0].message}`) as any; - error.rawError = errors; - error.rawData = data; - throw error; - } - resolve(data); - }) - .catch(error => { - // Don't retry if limit is reached or the error was not network/operational - if (retriesLeft === 1 || error instanceof ApiError) { - reject(error); - return; - } + return fetchUrl(endpoint, options) + .then(response => { + if (response.status !== 200 && response.status !== 400) { + throw new NetworkError(`Invalid status code: ${response.status}`); + } + return response.json(); + }) + .then(({ data, errors }) => { + if (errors) { + const error = new ApiError(`GraphQL Error: ${errors[0].message}`) as any; + error.rawError = errors; + error.rawData = data; + throw error; + } + resolve(data); + }) + .catch(error => { + // Don't retry if limit is reached or the error was not network/operational + if (retriesLeft === 1 || error instanceof ApiError) { + reject(error); + return; + } - setTimeout(() => { - retryFetch(endpoint, options, retriesLeft - 1, interval).then(resolve, reject); - }, interval); - }); - }); + setTimeout(() => { + retryFetch(endpoint, options, retriesLeft - 1, interval).then(resolve, reject); + }, interval); + }); +}); export class Transport { private endpoint: string; + private options: TransportOptions; constructor(endpoint: string, options: TransportOptions = {}) { @@ -80,10 +80,10 @@ export class Transport { this.options = { headers: { 'Content-Type': 'application/json', - ...options.headers + ...options.headers, }, timeout: 30000, - ...options + ...options, }; } @@ -91,7 +91,7 @@ export class Transport { return { method: 'POST', ...this.options, - body: JSON.stringify(payload) + body: JSON.stringify(payload), }; } diff --git a/services/api/src/commons/notificationCommons.ts b/services/api/src/commons/notificationCommons.ts new file mode 100644 index 0000000000..674371a19d --- /dev/null +++ b/services/api/src/commons/notificationCommons.ts @@ -0,0 +1,26 @@ +import * as R from 'ramda'; + +// Note: we're using integer representations of the +// severity in order to be able to use numeric comparisons +// when determining which messages to send + +export const notificationContentTypeToInt = R.cond([ + [R.equals('NONE'), R.always(0)], + [R.equals('UNKNOWN'), R.always(10)], + [R.equals('NEGLIGIBLE'), R.always(20)], + [R.equals('LOW'), R.always(30)], + [R.equals('MEDIUM'), R.always(40)], + [R.equals('HIGH'), R.always(50)], + [R.equals('CRITICAL'), R.always(60)], +]); + +export const notificationIntToContentType = R.cond([ + [R.equals('0'), R.always('NONE')], + [R.equals('10'), R.always('UNKNOWN')], + [R.equals('20'), R.always('NEGLIGIBLE')], + [R.equals('30'), R.always('LOW')], + [R.equals('40'), R.always('MEDIUM')], + [R.equals('50'), R.always('HIGH')], + [R.equals('60'), R.always('CRITICAL')], + [R.T, R.always('NONE')], +]); diff --git a/node-packages/commons/src/tasks.ts b/services/api/src/commons/tasks.ts similarity index 75% rename from node-packages/commons/src/tasks.ts rename to services/api/src/commons/tasks.ts index 5d9b7b8c37..2237caf6f2 100644 --- a/node-packages/commons/src/tasks.ts +++ b/services/api/src/commons/tasks.ts @@ -2,24 +2,19 @@ import * as R from 'ramda'; import { connect, AmqpConnectionManager, - ChannelWrapper + ChannelWrapper, } from 'amqp-connection-manager'; import { ConfirmChannel, ConsumeMessage } from 'amqplib'; +import sha1 from 'sha1'; import { logger } from './logs/local-logger'; import { EnvKeyValue, - EnvVariableScope, Kubernetes, Project, - getOpenShiftInfoForProject, getOpenShiftInfoForEnvironment, - getEnvironmentByIdWithVariables, getOrganizationById, - AutogeneratedRouteConfig, } from './api'; -import { InternalEnvVariableScope , RouteSource} from './types'; // @ts-ignore -import sha1 from 'sha1'; import { encodeJSONBase64, toNumber } from './util/func'; import { getConfigFromEnv } from './util/config'; @@ -42,9 +37,9 @@ interface DeathHandler { (msg: ConsumeMessage, error: Error): void; } -export let sendToLagoonTasks = function( +export let sendToLagoonTasks = function ( task: string, - payload?: any + payload?: any, ) { // TODO: Actually do something here? return payload && undefined; @@ -52,15 +47,15 @@ export let sendToLagoonTasks = function( export let sendToLagoonTasksMonitor = function sendToLagoonTasksMonitor( task: string, - payload?: any + payload?: any, ) { // TODO: Actually do something here? return payload && undefined; }; -export let sendToLagoonActions = function( +export let sendToLagoonActions = function ( task: string, - payload?: any + payload?: any, ) { // TODO: Actually do something here? return payload && undefined; @@ -84,28 +79,27 @@ const taskMonitorPrefetch = toNumber(getConfigFromEnv('TASKMONITOR_PREFETCH_COUN // * `api` // * `webhooks2tasks` const CI = getConfigFromEnv('CI', 'false'); -const overwriteActiveStandbyTaskImage = process.env.OVERWRITE_ACTIVESTANDBY_TASK_IMAGE +const overwriteActiveStandbyTaskImage = process.env.OVERWRITE_ACTIVESTANDBY_TASK_IMAGE; // add the lagoon actions queue publisher functions -export const initSendToLagoonActions = function() { +export const initSendToLagoonActions = function () { connection = connect( [`amqp://${rabbitmqUsername}:${rabbitmqPassword}@${rabbitmqHost}`], // @ts-ignore - { json: true } + { json: true }, ); - connection.on('connect', ({ url }) => - logger.verbose('lagoon-actions: Connected to %s', url, { - action: 'connected', - url - }) + connection.on('connect', ({ url }) => logger.verbose('lagoon-actions: Connected to %s', url, { + action: 'connected', + url, + }), ); connection.on('disconnect', params => // @ts-ignore logger.error('lagoon-actions: Not connected, error: %s', params.err.code, { action: 'disconnected', - reason: params - }) + reason: params, + }), ); const channelWrapperTasks: ChannelWrapper = connection.createChannel({ @@ -116,59 +110,58 @@ export const initSendToLagoonActions = function() { channel.assertExchange('lagoon-actions-delay', 'x-delayed-message', { durable: true, - arguments: { 'x-delayed-type': 'fanout' } + arguments: { 'x-delayed-type': 'fanout' }, }), channel.bindExchange('lagoon-actions', 'lagoon-actions-delay', ''), ]); - } + }, }); sendToLagoonActions = async ( task: string, - payload: any + payload: any, ): Promise => { try { const buffer = Buffer.from(JSON.stringify(payload)); await channelWrapperTasks.publish('lagoon-actions', '', buffer, { persistent: true, - appId: task + appId: task, }); logger.debug( `lagoon-actions: Successfully created action '${task}'`, - payload + payload, ); return `lagoon-actions: Successfully created action '${task}': ${JSON.stringify( - payload + payload, )}`; } catch (error) { logger.error('lagoon-actions: Error send to lagoon-actions exchange', { payload, - error + error, }); throw error; } }; -} +}; -export const initSendToLagoonTasks = function() { +export const initSendToLagoonTasks = function () { connection = connect( [`amqp://${rabbitmqUsername}:${rabbitmqPassword}@${rabbitmqHost}`], // @ts-ignore - { json: true } + { json: true }, ); - connection.on('connect', ({ url }) => - logger.verbose('lagoon-tasks: Connected to %s', url, { - action: 'connected', - url - }) + connection.on('connect', ({ url }) => logger.verbose('lagoon-tasks: Connected to %s', url, { + action: 'connected', + url, + }), ); connection.on('disconnect', params => // @ts-ignore logger.error('lagoon-tasks: Not connected, error: %s', params.err.code, { action: 'disconnected', - reason: params - }) + reason: params, + }), ); const channelWrapperTasks: ChannelWrapper = connection.createChannel({ @@ -179,49 +172,49 @@ export const initSendToLagoonTasks = function() { channel.assertExchange('lagoon-tasks-delay', 'x-delayed-message', { durable: true, - arguments: { 'x-delayed-type': 'fanout' } + arguments: { 'x-delayed-type': 'fanout' }, }), channel.bindExchange('lagoon-tasks', 'lagoon-tasks-delay', ''), // Exchange for task monitoring channel.assertExchange('lagoon-tasks-monitor', 'direct', { - durable: true + durable: true, }), channel.assertExchange( 'lagoon-tasks-monitor-delay', 'x-delayed-message', - { durable: true, arguments: { 'x-delayed-type': 'fanout' } } + { durable: true, arguments: { 'x-delayed-type': 'fanout' } }, ), channel.bindExchange( 'lagoon-tasks-monitor', 'lagoon-tasks-monitor-delay', - '' - ) + '', + ), ]); - } + }, }); sendToLagoonTasks = async ( task: string, - payload: any + payload: any, ): Promise => { try { const buffer = Buffer.from(JSON.stringify(payload)); await channelWrapperTasks.publish('lagoon-tasks', task, buffer, { - persistent: true + persistent: true, }); logger.debug( `lagoon-tasks: Successfully created task '${task}'`, - payload + payload, ); return `lagoon-tasks: Successfully created task '${task}': ${JSON.stringify( - payload + payload, )}`; } catch (error) { logger.error('lagoon-tasks: Error send to lagoon-tasks exchange', { payload, - error + error, }); throw error; } @@ -229,75 +222,74 @@ export const initSendToLagoonTasks = function() { sendToLagoonTasksMonitor = async ( task: string, - payload: any + payload: any, ): Promise => { try { const buffer = Buffer.from(JSON.stringify(payload)); await channelWrapperTasks.publish('lagoon-tasks-monitor', task, buffer, { - persistent: true + persistent: true, }); logger.debug( `lagoon-tasks-monitor: Successfully created monitor '${task}'`, - payload + payload, ); return `lagoon-tasks-monitor: Successfully created task monitor '${task}': ${JSON.stringify( - payload + payload, )}`; } catch (error) { logger.error( 'lagoon-tasks-monitor: Error send to lagoon-tasks-monitor exchange', { payload, - error - } + error, + }, ); throw error; } }; -} +}; -export const createTaskMonitor = async function(task: string, payload: any) { +export const createTaskMonitor = async function (task: string, payload: any) { return sendToLagoonTasksMonitor(task, payload); -} +}; // makes strings "safe" if it is to be used in something dns related -export const makeSafe = (string: string): string => - string.toLocaleLowerCase().replace(/[^0-9a-z-]/g,'-') +export const makeSafe = (string: string): string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g, '-'); // function to truncate the environment name as required to fit within kubernetes namespace limits export const truncateEnvironmentName = (projectName: string, environmentName: string): string => { - var envName = makeSafe(environmentName) - var overlength = 58 - projectName.length; - if ( envName.length > overlength ) { - var hash = sha1(envName).substring(0,4) - envName = envName.substring(0, overlength-5) - envName = envName.concat('-' + hash) + let envName = makeSafe(environmentName); + const overlength = 58 - projectName.length; + if (envName.length > overlength) { + const hash = sha1(envName).substring(0, 4); + envName = envName.substring(0, overlength - 5); + envName = envName.concat(`-${hash}`); } return envName; -} +}; // function to seed the initial namespace for use in export const seedNamespace = (projectName: string, environmentName: string): string => { - var envName = truncateEnvironmentName(projectName, environmentName) - return makeSafe(`${projectName}-${envName}`) -} + const envName = truncateEnvironmentName(projectName, environmentName); + return makeSafe(`${projectName}-${envName}`); +}; // creates the restore job configuration for use in the misc task const restoreConfig = (name: string, backupId: string, backupS3Config: any, restoreS3Config: any) => { - let config = { + const config = { metadata: { - name + name, }, spec: { snapshot: backupId, restoreMethod: { - s3: restoreS3Config ? restoreS3Config : {}, + s3: restoreS3Config || {}, }, backend: { s3: backupS3Config, repoPasswordSecretRef: { key: 'repo-pw', - name: 'baas-repo-pw' + name: 'baas-repo-pw', }, }, }, @@ -310,9 +302,8 @@ export const getBaasBucketName = async ( project: Pick & { envVariables: Pick[]; }, - deploytarget: Pick + deploytarget: Pick, ): Promise<[string | undefined, false] | [string, true]> => { - // Bucket name defined in API env var always takes precedence. const envVarBucketName = getEnvVarValue(project.envVariables, 'LAGOON_BAAS_BUCKET_NAME'); if (envVarBucketName) { @@ -326,58 +317,58 @@ export const getBaasBucketName = async ( } return [undefined, false]; -} +}; -export const createTaskTask = async function(taskData: any) { +export const createTaskTask = async function (taskData: any) { const { project } = taskData; // inject variables into tasks the same way it is in builds taskData.project.variables = { environment: taskData.envVars, - } + }; if (project.organization != null) { const curOrg = await getOrganizationById(project.organization); const organization = { name: curOrg.name, id: curOrg.id, - } - taskData.project.organization = organization + }; + taskData.project.organization = organization; } // since controllers queues are named, we have to send it to the right tasks queue // do that here by querying which deploytarget the environment uses const result = await getOpenShiftInfoForEnvironment(taskData.environment.id); - const deployTarget = result.environment.openshift.name - return sendToLagoonTasks(deployTarget+":jobs", taskData); -} + const deployTarget = result.environment.openshift.name; + return sendToLagoonTasks(`${deployTarget}:jobs`, taskData); +}; -export const createMiscTask = async function(taskData: any) { +export const createMiscTask = async function (taskData: any) { const { key, - data: { project } + data: { project }, } = taskData; - let updatedKey = `deploytarget:${key}`; + const updatedKey = `deploytarget:${key}`; // determine the deploy target (openshift/kubernetes) for the task to go to // we get this from the environment const result = await getOpenShiftInfoForEnvironment(taskData.data.environment.id); - const deployTarget = result.environment.openshift.name + const deployTarget = result.environment.openshift.name; // this is the json structure for sending a misc task to the controller // there are some additional bits that can be adjusted, and these are done in the switch below on `updatedKey` - var miscTaskData: any = { + const miscTaskData: any = { misc: {}, key: updatedKey, environment: { name: taskData.data.environment.name, - openshiftProjectName: taskData.data.environment.openshiftProjectName + openshiftProjectName: taskData.data.environment.openshiftProjectName, }, project: { - name: taskData.data.project.name + name: taskData.data.project.name, }, task: taskData.data.task, - advancedTask: {} - } + advancedTask: {}, + }; switch (updatedKey) { case 'deploytarget:restic:backup:restore': // Handle setting up the configuration for a restic restoration task @@ -385,116 +376,118 @@ export const createMiscTask = async function(taskData: any) { const restoreName = `restore-${R.slice(0, 7, taskData.data.backup.backupId)}-${randRestoreId}`; // Handle custom backup configurations - let backupS3Config = {} + let backupS3Config = {}; if ( - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_ENDPOINT') && - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_BUCKET') && - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_ACCESS_KEY') && - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_SECRET_KEY') + hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_ENDPOINT') + && hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_BUCKET') + && hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_ACCESS_KEY') + && hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_SECRET_KEY') ) { backupS3Config = { endpoint: getEnvVarValue(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_ENDPOINT'), bucket: getEnvVarValue(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_BACKUP_BUCKET'), accessKeyIDSecretRef: { - name: "lagoon-baas-custom-backup-credentials", - key: "access-key" + name: 'lagoon-baas-custom-backup-credentials', + key: 'access-key', }, secretAccessKeySecretRef: { - name: "lagoon-baas-custom-backup-credentials", - key: "secret-key" - } - } + name: 'lagoon-baas-custom-backup-credentials', + key: 'secret-key', + }, + }; } else { // Parse out the baasBucketName for any migrated projects // check if the project is configured for a shared baas bucket - let [bucketName, isSharedBucket] = await getBaasBucketName(result.environment.project, result.environment.openshift) + const [bucketNameRaw, isSharedBucket] = await getBaasBucketName(result.environment.project, result.environment.openshift); + let bucketName = bucketNameRaw; if (isSharedBucket) { // if it is a shared bucket, add the repo key to it too for restores - bucketName = `${bucketName}/baas-${makeSafe(taskData.data.project.name)}` + bucketName = `${bucketName}/baas-${makeSafe(taskData.data.project.name)}`; } backupS3Config = { - bucket: bucketName ?? `baas-${makeSafe(taskData.data.project.name)}` - } + bucket: bucketName ?? `baas-${makeSafe(taskData.data.project.name)}`, + }; } // Handle custom restore configurations - let restoreS3Config = {} + let restoreS3Config = {}; if ( - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_ENDPOINT') && - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_BUCKET') && - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_ACCESS_KEY') && - hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_SECRET_KEY') + hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_ENDPOINT') + && hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_BUCKET') + && hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_ACCESS_KEY') + && hasEnvVar(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_SECRET_KEY') ) { restoreS3Config = { endpoint: getEnvVarValue(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_ENDPOINT'), bucket: getEnvVarValue(result.environment.project.envVariables, 'LAGOON_BAAS_CUSTOM_RESTORE_BUCKET'), accessKeyIDSecretRef: { - name: "lagoon-baas-custom-restore-credentials", - key: "access-key" + name: 'lagoon-baas-custom-restore-credentials', + key: 'access-key', }, secretAccessKeySecretRef: { - name: "lagoon-baas-custom-restore-credentials", - key: "secret-key" - } - } + name: 'lagoon-baas-custom-restore-credentials', + key: 'secret-key', + }, + }; } // generate the restore CRD - const restoreConf = restoreConfig(restoreName, taskData.data.backup.backupId, backupS3Config, restoreS3Config) - //logger.info(restoreConf) - miscTaskData.misc.miscResource = encodeJSONBase64(restoreConf) + const restoreConf = restoreConfig(restoreName, taskData.data.backup.backupId, backupS3Config, restoreS3Config); + // logger.info(restoreConf) + miscTaskData.misc.miscResource = encodeJSONBase64(restoreConf); break; case 'deploytarget:task:activestandby': // handle setting up the task configuration for running the active/standby switch // this uses the `advanced task` system in the controllers // generate out custom json payload to send to the advanced task + // eslint-disable-next-line vars-on-top, no-var var jsonPayload: any = { productionEnvironment: taskData.data.productionEnvironment.name, standbyEnvironment: taskData.data.environment.name, sourceNamespace: makeSafe(taskData.data.environment.openshiftProjectName), - destinationNamespace: makeSafe(taskData.data.productionEnvironment.openshiftProjectName) - } + destinationNamespace: makeSafe(taskData.data.productionEnvironment.openshiftProjectName), + }; // set the task data up miscTaskData.advancedTask.JSONPayload = encodeJSONBase64(jsonPayload); // use this image to run the task - let taskImage = "" + let taskImage = ''; // choose which task image to use - if (CI == "true") { - taskImage = "172.17.0.1:5000/lagoon/task-activestandby:latest" + if (CI === 'true') { + taskImage = '172.17.0.1:5000/lagoon/task-activestandby:latest'; } else if (overwriteActiveStandbyTaskImage) { // allow to overwrite the image we use via OVERWRITE_ACTIVESTANDBY_TASK_IMAGE env variable - taskImage = overwriteActiveStandbyTaskImage + taskImage = overwriteActiveStandbyTaskImage; } else { - taskImage = `uselagoon/task-activestandby:${getConfigFromEnv('LAGOON_VERSION', 'unknown')}` + taskImage = `uselagoon/task-activestandby:${getConfigFromEnv('LAGOON_VERSION', 'unknown')}`; } - miscTaskData.advancedTask.runnerImage = taskImage + miscTaskData.advancedTask.runnerImage = taskImage; // miscTaskData.advancedTask.runnerImage = "shreddedbacon/runner:latest" break; case 'deploytarget:task:advanced': // inject variables into advanced tasks the same way it is in builds and standard tasks miscTaskData.project.variables = { environment: taskData.envVars, - } - miscTaskData.advancedTask = taskData.data.advancedTask + }; + miscTaskData.advancedTask = taskData.data.advancedTask; break; case 'deploytarget:task:cancel': // task cancellation is just a standard unmodified message - miscTaskData.misc = taskData.data.task + miscTaskData.misc = taskData.data.task; break; case 'deploytarget:build:cancel': // build cancellation is just a standard unmodified message - miscTaskData.misc = taskData.data.build + miscTaskData.misc = taskData.data.build; break; case 'deploytarget:environment:idling': // environment idling is used to handle idling or unidling of an an environment - miscTaskData.misc.miscResource = encodeJSONBase64(taskData.data.idling) + miscTaskData.misc.miscResource = encodeJSONBase64(taskData.data.idling); break; case 'deploytarget:environment:service': // environment service is used to handle stop, start, or restarting of a service in an environment - miscTaskData.misc.miscResource = encodeJSONBase64(taskData.data.lagoonService) + miscTaskData.misc.miscResource = encodeJSONBase64(taskData.data.lagoonService); break; default: - miscTaskData.misc = taskData.data.build + miscTaskData.misc = taskData.data.build; break; } // send the task to the queue @@ -503,17 +496,17 @@ export const createMiscTask = async function(taskData: any) { const organization = { name: curOrg.name, id: curOrg.id, - } - miscTaskData.project.organization = organization + }; + miscTaskData.project.organization = organization; } - return sendToLagoonTasks(deployTarget+':misc', miscTaskData); -} + return sendToLagoonTasks(`${deployTarget}:misc`, miscTaskData); +}; -export const consumeTasks = async function( +export const consumeTasks = async function ( taskQueueName: string, messageConsumer: MessageConsumer, retryHandler: RetryHandler, - deathHandler: DeathHandler + deathHandler: DeathHandler, ) { const onMessage = async (msg: ConsumeMessage) => { try { @@ -523,7 +516,7 @@ export const consumeTasks = async function( // We land here if the messageConsumer has an error that it did not itslef handle. // This is how the consumer informs us that we it would like to retry the message in a couple of seconds - const headers = msg.properties.headers || {'x-retry': 1} + const headers = msg.properties.headers || { 'x-retry': 1 }; const retryCount = headers['x-retry'] ? headers['x-retry'] + 1 : 1; @@ -542,7 +535,7 @@ export const consumeTasks = async function( } catch (retryError) { // intentionally empty as we don't want to fail and not requeue our message just becase the retryHandler fails logger.info( - `lagoon-tasks: retryHandler for ${taskQueueName} failed with ${retryError}, will continue to retry the message anyway.` + `lagoon-tasks: retryHandler for ${taskQueueName} failed with ${retryError}, will continue to retry the message anyway.`, ); } @@ -555,9 +548,9 @@ export const consumeTasks = async function( headers: { ...msg.properties.headers, 'x-delay': retryDelayMilisecs, - 'x-retry': retryCount + 'x-retry': retryCount, }, - persistent: true + persistent: true, }; // publishing a new message with the same content as the original message but into the `lagoon-tasks-delay` exchange, @@ -566,7 +559,7 @@ export const consumeTasks = async function( 'lagoon-tasks-delay', msg.fields.routingKey, msg.content, - retryMsgOptions + retryMsgOptions, ); // acknologing the existing message, we cloned it and is not necessary anymore @@ -581,21 +574,21 @@ export const consumeTasks = async function( channel.bindQueue( `lagoon-tasks:${taskQueueName}`, 'lagoon-tasks', - taskQueueName + taskQueueName, ), channel.prefetch(taskPrefetch), channel.consume(`lagoon-tasks:${taskQueueName}`, onMessage, { - noAck: false - }) + noAck: false, + }), ]); - } + }, }); -} +}; -export const consumeTaskMonitor = async function( +export const consumeTaskMonitor = async function ( taskMonitorQueueName: string, messageConsumer: MessageConsumer, - deathHandler: DeathHandler + deathHandler: DeathHandler, ) { const onMessage = async (msg: ConsumeMessage) => { try { @@ -605,7 +598,7 @@ export const consumeTaskMonitor = async function( // We land here if the messageConsumer has an error that it did not itslef handle. // This is how the consumer informs us that we it would like to retry the message in a couple of seconds - const headers = msg.properties.headers || {'x-retry': 1} + const headers = msg.properties.headers || { 'x-retry': 1 }; const retryCount = headers['x-retry'] ? headers['x-retry'] + 1 : 1; @@ -633,9 +626,9 @@ export const consumeTaskMonitor = async function( headers: { ...msg.properties.headers, 'x-delay': retryDelayMilisecs, - 'x-retry': retryCount + 'x-retry': retryCount, }, - persistent: true + persistent: true, }; // publishing a new message with the same content as the original message but into the `lagoon-tasks-delay` exchange, @@ -644,7 +637,7 @@ export const consumeTaskMonitor = async function( 'lagoon-tasks-monitor-delay', msg.fields.routingKey, msg.content, - retryMsgOptions + retryMsgOptions, ); // acknologing the existing message, we cloned it and is not necessary anymore @@ -656,40 +649,40 @@ export const consumeTaskMonitor = async function( setup(channel: ConfirmChannel) { return Promise.all([ channel.assertQueue(`lagoon-tasks-monitor:${taskMonitorQueueName}`, { - durable: true + durable: true, }), channel.bindQueue( `lagoon-tasks-monitor:${taskMonitorQueueName}`, 'lagoon-tasks-monitor', - taskMonitorQueueName + taskMonitorQueueName, ), channel.prefetch(taskMonitorPrefetch), channel.consume( `lagoon-tasks-monitor:${taskMonitorQueueName}`, onMessage, - { noAck: false } - ) + { noAck: false }, + ), ]); - } + }, }); -} +}; - // leverages the `misc` queue to handle retention policy only messages to controller - // this is essentially a clone of createMiscTask, but specifically for retention policies -export const createRetentionPolicyTask = async function(policyData: any) { - var policyPayload: any = { +// leverages the `misc` queue to handle retention policy only messages to controller +// this is essentially a clone of createMiscTask, but specifically for retention policies +export const createRetentionPolicyTask = async function (policyData: any) { + const policyPayload: any = { key: `deploytarget:${policyData.key}`, - misc: {} - } + misc: {}, + }; switch (`deploytarget:${policyData.key}`) { case 'deploytarget:harborpolicy:update': // remote-controller has a basic payload resource under `misc` called `miscResource` which can store bytes // so this b64 encodes the payload event and inserts it into the miscResource so that the remote-controller will understand it - const payloadBytes = new Buffer(JSON.stringify(policyData.data.event).replace(/\\n/g, "\n")).toString('base64') - policyPayload.misc.miscResource = payloadBytes + const payloadBytes = Buffer.from(JSON.stringify(policyData.data.event).replace(/\n/g, '\n')).toString('base64'); + policyPayload.misc.miscResource = payloadBytes; break; default: break; } - return sendToLagoonTasks(policyData.data.target+':misc', policyPayload); -} + return sendToLagoonTasks(`${policyData.data.target}:misc`, policyPayload); +}; diff --git a/node-packages/commons/src/types.ts b/services/api/src/commons/types.ts similarity index 100% rename from node-packages/commons/src/types.ts rename to services/api/src/commons/types.ts diff --git a/node-packages/commons/src/util/config.ts b/services/api/src/commons/util/config.ts similarity index 57% rename from node-packages/commons/src/util/config.ts rename to services/api/src/commons/util/config.ts index 4c61c4a4e5..7a8a9057bf 100644 --- a/node-packages/commons/src/util/config.ts +++ b/services/api/src/commons/util/config.ts @@ -4,11 +4,9 @@ import { isNotNil, isNotEmpty } from './func'; export const getConfigOr = ( data: any, name: string, - fallback: string + fallback: string, ): string => propOr(fallback, name, data); -export const getConfigFromEnv = (name: string, fallback: string = ''): string => - getConfigOr(process.env, name, fallback); +export const getConfigFromEnv = (name: string, fallback: string = ''): string => getConfigOr(process.env, name, fallback); -export const envHasConfig = (name: string): boolean => - both(isNotNil, isNotEmpty)(prop(name, process.env)); +export const envHasConfig = (name: string): boolean => both(isNotNil, isNotEmpty)(prop(name, process.env)); diff --git a/node-packages/commons/src/util/func.ts b/services/api/src/commons/util/func.ts similarity index 74% rename from node-packages/commons/src/util/func.ts rename to services/api/src/commons/util/func.ts index f6ca62e363..dbd72f2dda 100644 --- a/node-packages/commons/src/util/func.ts +++ b/services/api/src/commons/util/func.ts @@ -1,31 +1,25 @@ -import { unless, is, isNil, isEmpty, partialRight, complement } from 'ramda'; +import { + unless, is, isNil, isEmpty, partialRight, complement, +} from 'ramda'; import { getConfigFromEnv } from './config'; export const isNumber = is(Number); export const isArray = is(Array); -export const toNumber = (input: string | number): number => - unless(isNumber, partialRight(parseInt, [10]), input) as number; +export const toNumber = (input: string | number): number => unless(isNumber, partialRight(parseInt, [10]), input) as number; export const notArray = complement(isArray); export const isNotNil = complement(isNil); export const isNotEmpty = complement(isEmpty); -export const asyncPipe = - (...functions: any[]) => - (input: any) => - functions.reduce((chain, func) => chain.then(func), Promise.resolve(input)); +export const asyncPipe = (...functions: any[]) => (input: any) => functions.reduce((chain, func) => chain.then(func), Promise.resolve(input)); export const jsonMerge = >( a: T[], b: T[], prop: string, ) => { - var reduced = a.filter(function (aitem) { - return !b.find(function (bitem) { - return aitem[prop] === bitem[prop]; - }); - }); + const reduced = a.filter((aitem) => !b.find((bitem) => aitem[prop] === bitem[prop])); return reduced.concat(b); }; @@ -35,20 +29,15 @@ export const jsonMerge = >( // a1 = [1,2,3,4] // a2 = [1,2,3,5] // arrayDiff(a1,a2) = [4] -export const arrayDiff = (a: Array, b: Array) => - a.filter((e) => !b.includes(e)); +export const arrayDiff = (a: Array, b: Array) => a.filter((e) => !b.includes(e)); -export const encodeBase64 = (data: string): string => - Buffer.from(data, 'utf8').toString('base64'); +export const encodeBase64 = (data: string): string => Buffer.from(data, 'utf8').toString('base64'); -export const decodeBase64 = (data: string): string => - Buffer.from(data, 'base64').toString('utf8'); +export const decodeBase64 = (data: string): string => Buffer.from(data, 'base64').toString('utf8'); -export const encodeJSONBase64 = (data: any): string => - encodeBase64(JSON.stringify(data)); +export const encodeJSONBase64 = (data: any): string => encodeBase64(JSON.stringify(data)); -export const decodeJSONBase64 = (data: string): any => - JSON.parse(decodeBase64(data)); +export const decodeJSONBase64 = (data: string): any => JSON.parse(decodeBase64(data)); export const getErrorMessage = (err: unknown): string => { let msg = `unknown error (${typeof err})`; @@ -60,7 +49,7 @@ export const getErrorMessage = (err: unknown): string => { } return msg; -} +}; interface PublicKeyResponse { error?: string; @@ -88,7 +77,7 @@ export async function validateKey( key: string, type: 'public' | 'private', ): Promise { - let response = await fetch( + const response = await fetch( `http://${getConfigFromEnv('SIDECAR_HANDLER_HOST', 'localhost')}:3333/validate/${type}`, { method: 'POST', @@ -105,14 +94,13 @@ export async function validateKey( if (type === 'public') { return (await response.json()) as PublicKeyResponse; - } else { - return (await response.json()) as PrivateKeyResponse; } + return (await response.json()) as PrivateKeyResponse; } // helper that will use the crypto handler service to generate a private key with associated public key export async function generatePrivateKey(): Promise { - let response = await fetch( + const response = await fetch( `http://${getConfigFromEnv('SIDECAR_HANDLER_HOST', 'localhost')}:3333/generate/ed25519`, ); @@ -125,33 +113,33 @@ export async function generatePrivateKey(): Promise { // https://github.com/kubernetes/apimachinery/blob/v0.34.1/pkg/util/validation/validation.go#L219 // javascript implementation of the function used by kubernetes to verify ingress -const dns1123LabelFmt: string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"; +const dns1123LabelFmt: string = '[a-z0-9]([-a-z0-9]*[a-z0-9])?'; const dns1123SubdomainFmt: string = `${dns1123LabelFmt}(\\.${dns1123LabelFmt})*`; const dns1123SubdomainMaxLength: number = 253; export function isDNS1123Subdomain(value: string): boolean { if (value.length > dns1123SubdomainMaxLength) { - return false; + return false; } const dns1123SubdomainRegexp = new RegExp(dns1123SubdomainFmt); if (!dns1123SubdomainRegexp.test(value)) { - return false; + return false; } - return true; + return true; } // https://github.com/kubernetes/apimachinery/blob/v0.34.1/pkg/util/validation/validation.go#L41 // javascript implementation of the function used by kubernetes to verify annotation/label keys -const labelKeyCharFmt = "[A-Za-z0-9]"; -const labelKeyExtCharFmt = "[-A-Za-z0-9_.]"; +const labelKeyCharFmt = '[A-Za-z0-9]'; +const labelKeyExtCharFmt = '[-A-Za-z0-9_.]'; const labelKeyFmt = `(${labelKeyCharFmt}${labelKeyExtCharFmt}*)?${labelKeyCharFmt}`; const labelKeyErrMsg = "must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"; const labelKeyMaxLength = 63; const labelKeyRegexp = new RegExp(`^${labelKeyFmt}$`); export function isLabelKey(value: string) { const errs = []; - const parts = value.split("/"); + const parts = value.split('/'); let name; switch (parts.length) { case 1: @@ -161,7 +149,7 @@ export function isLabelKey(value: string) { const prefix = parts[0]; name = parts[1]; if (prefix.length === 0) { - errs.push("prefix part cannot be empty"); + errs.push('prefix part cannot be empty'); } else if (!isDNS1123Subdomain(prefix)) { errs.push(`prefix part ${prefix} must be a valid DNS subdomain`); } @@ -170,7 +158,7 @@ export function isLabelKey(value: string) { return errs.concat(`a valid label key ${labelKeyErrMsg} with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')`); } if (name.length === 0) { - errs.push("name part cannot be empty"); + errs.push('name part cannot be empty'); } else if (name.length > labelKeyMaxLength) { errs.push(`name part exceeds maximum length of ${labelKeyMaxLength}`); } @@ -178,4 +166,4 @@ export function isLabelKey(value: string) { errs.push(`name part ${labelKeyErrMsg}`); } return errs; -} \ No newline at end of file +} diff --git a/node-packages/commons/src/util/lagoon.ts b/services/api/src/commons/util/lagoon.ts similarity index 65% rename from node-packages/commons/src/util/lagoon.ts rename to services/api/src/commons/util/lagoon.ts index 830daac8a7..08fd7936a2 100644 --- a/node-packages/commons/src/util/lagoon.ts +++ b/services/api/src/commons/util/lagoon.ts @@ -1,14 +1,11 @@ import { getConfigFromEnv } from './config'; import { EnvKeyValue } from '../api'; -export const generateBuildId = () => - `lagoon-build-${Math.random().toString(36).substring(7)}`; +export const generateBuildId = () => `lagoon-build-${Math.random().toString(36).substring(7)}`; -export const generateVariableOnlyBuildId = () => - `lagoon-variables-${Math.random().toString(36).substring(7)}`; +export const generateVariableOnlyBuildId = () => `lagoon-variables-${Math.random().toString(36).substring(7)}`; -export const generateTaskName = () => - `lagoon-task-${Math.random().toString(36).substring(7)}`; +export const generateTaskName = () => `lagoon-task-${Math.random().toString(36).substring(7)}`; // Find a URL in `process.env.LAGOON_ROUTES` or return a fallback. export const getLagoonRouteFromEnv = ( @@ -19,7 +16,7 @@ export const getLagoonRouteFromEnv = ( const route = routes.find(route => routeTest.test(route)); return route ?? fallback; -} +}; export const hasEnvVar = ( envVars: Pick[], @@ -37,5 +34,4 @@ export const hasEnvVar = ( export const getEnvVarValue = ( envVars: Pick[], name: string, -): string | undefined => - envVars.find((envVar) => envVar.name === name)?.value; +): string | undefined => envVars.find((envVar) => envVar.name === name)?.value; diff --git a/services/api/src/data/mock-data.js b/services/api/src/data/mock-data.js index e61a42625a..8811eeab17 100644 --- a/services/api/src/data/mock-data.js +++ b/services/api/src/data/mock-data.js @@ -1,50 +1,50 @@ export const packages = [ - 'ansible', - 'apache-log', - 'awl', - 'cacti', - 'chromium', - 'commons-configuration2', - 'consul', - 'dom4j', - 'drupal', - 'file-roller', - 'glibc', - 'golang-go.crypto', - 'graphicsmagick', - 'http-parser', - 'imagemagick', - 'jruby', - 'ksh', - 'libmicrodns', - 'libxml-security-java', - 'linux', - 'lucene-solr', - 'lxc-templates', - 'matrix-synapse', - 'mbedtls', - 'netty', - 'nginx', - 'node-yarnpkg', - 'nodejs', - 'nss', - 'openjdk-11', - 'phantomjs', - 'php7.1', - 'php7.3', - 'python', - 'rmysql', - 'ruby-json-jwt', - 'ruby-omniauth', - 'salt', - 'shiro', - 'slirp', - 'squid', - 'ssvnc', - 'thrift', - 'tomcat9', - 'trafficserver', - 'varnish', - 'xerces-c', - 'yubikey-val', -]; \ No newline at end of file + 'ansible', + 'apache-log', + 'awl', + 'cacti', + 'chromium', + 'commons-configuration2', + 'consul', + 'dom4j', + 'drupal', + 'file-roller', + 'glibc', + 'golang-go.crypto', + 'graphicsmagick', + 'http-parser', + 'imagemagick', + 'jruby', + 'ksh', + 'libmicrodns', + 'libxml-security-java', + 'linux', + 'lucene-solr', + 'lxc-templates', + 'matrix-synapse', + 'mbedtls', + 'netty', + 'nginx', + 'node-yarnpkg', + 'nodejs', + 'nss', + 'openjdk-11', + 'phantomjs', + 'php7.1', + 'php7.3', + 'python', + 'rmysql', + 'ruby-json-jwt', + 'ruby-omniauth', + 'salt', + 'shiro', + 'slirp', + 'squid', + 'ssvnc', + 'thrift', + 'tomcat9', + 'trafficserver', + 'varnish', + 'xerces-c', + 'yubikey-val', +]; diff --git a/services/api/src/fragmentMatchers.js b/services/api/src/fragmentMatchers.js index b95985b219..7d9f62580a 100644 --- a/services/api/src/fragmentMatchers.js +++ b/services/api/src/fragmentMatchers.js @@ -6,11 +6,11 @@ const fs = require('fs'); const TOKEN = ''; // set this prior to running -fetch(`http://localhost:3000/graphql`, { +fetch('http://localhost:3000/graphql', { method: 'POST', headers: { Authorization: `Bearer ${TOKEN}`, - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, body: JSON.stringify({ variables: {}, @@ -43,4 +43,4 @@ fetch(`http://localhost:3000/graphql`, { console.log('Fragment types successfully extracted!'); } }); - }); \ No newline at end of file + }); diff --git a/services/api/src/gitlab-sync/groups.ts b/services/api/src/gitlab-sync/groups.ts index e90e65845e..c24cf72602 100644 --- a/services/api/src/gitlab-sync/groups.ts +++ b/services/api/src/gitlab-sync/groups.ts @@ -1,19 +1,19 @@ import * as R from 'ramda'; -import * as gitlabApi from '@lagoon/commons/dist/gitlab/api'; +import * as gitlabApi from '../commons/gitlab/api'; import { sanitizeGroupName, addGroupWithParent, addGroup, - addUserToGroup -} from '@lagoon/commons/dist/api'; -import { logger } from '@lagoon/commons/dist/logs/local-logger'; + addUserToGroup, +} from '../commons/api'; +import { logger } from '../commons/logs/local-logger'; -interface GitlabGroup { +interface _GitlabGroup { id: number, name: string, full_path: string, parent_id: number, -}; +} const groupExistsRegex = /Group.*?exists/; const sortGroupsByHierarchy = R.sortBy(R.path(['full_path'])); @@ -53,7 +53,7 @@ const syncGroup = async group => { (async () => { const allGroups = await gitlabApi.getAllGroups(); - let groupsQueue = sortGroupsByHierarchy(allGroups).map(group => ({ group, retries: 0})); + const groupsQueue = sortGroupsByHierarchy(allGroups).map(group => ({ group, retries: 0 })); logger.info(`Syncing ${allGroups.length} groups`); @@ -65,12 +65,11 @@ const syncGroup = async group => { if (retries < 3) { logger.warn(`Error syncing, adding to end of queue: ${err.message}`); groupsQueue.push({ group, retries: retries + 1 }); - } - else { + } else { logger.error(`Sync failed: ${err.message}`); } } } logger.info('Sync completed'); -})() +})(); diff --git a/services/api/src/gitlab-sync/projects.ts b/services/api/src/gitlab-sync/projects.ts index 0f42e99bf2..1f126bf768 100644 --- a/services/api/src/gitlab-sync/projects.ts +++ b/services/api/src/gitlab-sync/projects.ts @@ -1,14 +1,14 @@ import * as R from 'ramda'; -import * as gitlabApi from '@lagoon/commons/dist/gitlab/api'; +import * as gitlabApi from '../commons/gitlab/api'; import { sanitizeGroupName, sanitizeProjectName, addProject, getProjectByName, addGroupToProject, - addUserToGroup -} from '@lagoon/commons/dist/api'; -import { logger } from '@lagoon/commons/dist/logs/local-logger'; + addUserToGroup, +} from '../commons/api'; +import { logger } from '../commons/logs/local-logger'; import { validateKey } from '../util/func'; interface GitlabProject { @@ -20,7 +20,7 @@ interface GitlabProject { path: string, full_path: string, }, -}; +} const projectExistsRegex = /Project already exists/; const convertRoleNumberToString = R.cond([ @@ -32,13 +32,15 @@ const convertRoleNumberToString = R.cond([ ]); const syncProject = async (project) => { - const { id, path, ssh_url_to_repo: gitUrl, namespace } = project; + const { + id, path, ssh_url_to_repo: gitUrl, namespace, + } = project; const projectName = sanitizeProjectName(path); const openshift = 1; - const productionenvironment = "master"; + const productionenvironment = 'master'; logger.debug(`Processing ${projectName}`); - if (project.namespace.kind != 'group') { + if (project.namespace.kind !== 'group') { logger.info(`Skipping creation of project ${projectName}: not in group namespace`); return; } @@ -56,13 +58,13 @@ const syncProject = async (project) => { } try { - const publickey = await validateKey(R.prop('privateKey', lagoonProject), "private") + const publickey = await validateKey(R.prop('privateKey', lagoonProject), 'private'); if (!publickey?.publickey) { throw new Error('Could not validate key'); } - await gitlabApi.addDeployKeyToProject(id, publickey['publickey']); + await gitlabApi.addDeployKeyToProject(id, publickey.publickey); } catch (err) { if (!err.message.includes('has already been taken')) { throw new Error(`Could not add deploy_key to gitlab project ${id}, reason: ${err}`); @@ -84,7 +86,7 @@ const syncProject = async (project) => { (async () => { const allProjects = await gitlabApi.getAllProjects() as GitlabProject[]; - let projectsQueue = allProjects.map(project => ({ project, retries: 0})); + const projectsQueue = allProjects.map(project => ({ project, retries: 0 })); logger.info(`Syncing ${allProjects.length} projects`); @@ -96,12 +98,11 @@ const syncProject = async (project) => { if (retries < 3) { logger.warn(`Error syncing, adding to end of queue: ${err.message}`); projectsQueue.push({ project, retries: retries + 1 }); - } - else { + } else { logger.error(`Sync failed: ${err.message}`); } } } logger.info('Sync completed'); -})() +})(); diff --git a/services/api/src/gitlab-sync/users.ts b/services/api/src/gitlab-sync/users.ts index d1eb9f601f..b0f6fcea09 100644 --- a/services/api/src/gitlab-sync/users.ts +++ b/services/api/src/gitlab-sync/users.ts @@ -1,7 +1,7 @@ import * as R from 'ramda'; -import * as gitlabApi from '@lagoon/commons/dist/gitlab/api'; -import { addUser, updateUser } from '@lagoon/commons/dist/api'; -import { logger } from '@lagoon/commons/dist/logs/local-logger'; +import * as gitlabApi from '../commons/gitlab/api'; +import { addUser, updateUser } from '../commons/api'; +import { logger } from '../commons/logs/local-logger'; const usernameExistsRegex = /Username.*?exists/; @@ -13,8 +13,8 @@ const usernameExistsRegex = /Username.*?exists/; for (const user of allUsers) { logger.debug(`Processing ${user.email}`); - let firstName = user.name, - lastName; + let firstName = user.name; + let lastName; if (user.name.includes(' ')) { const nameParts = user.name.split(' '); firstName = R.head(nameParts) as string; @@ -41,7 +41,7 @@ const usernameExistsRegex = /Username.*?exists/; firstName, lastName, gitlabId: user.id, - } + }, ); } catch (err) { logger.error(`Could not sync (update) gitlab user ${user.email} id ${user.id}: ${err.message}`); @@ -51,4 +51,4 @@ const usernameExistsRegex = /Username.*?exists/; } logger.info('Sync completed'); -})() +})(); diff --git a/services/api/src/helpers/reset-project-keys.ts b/services/api/src/helpers/reset-project-keys.ts index 9cf4093d9c..1441b1874a 100644 --- a/services/api/src/helpers/reset-project-keys.ts +++ b/services/api/src/helpers/reset-project-keys.ts @@ -1,7 +1,7 @@ import * as R from 'ramda'; -import { logger } from '@lagoon/commons/dist/logs/local-logger'; -import { sanitizeGroupName } from '@lagoon/commons/dist/api'; -import * as gitlabApi from '@lagoon/commons/dist/gitlab/api'; +import { logger } from '../commons/logs/local-logger'; +import { sanitizeGroupName } from '../commons/api'; +import * as gitlabApi from '../commons/gitlab/api'; import { getKeycloakAdminClient } from '../clients/keycloak-admin'; import { sqlClientPool } from '../clients/sqlClient'; import { query } from '../util/db'; @@ -39,8 +39,7 @@ interface GitlabProject { return; } - const allGitlabProjects = - (await gitlabApi.getAllProjects()) as GitlabProject[]; + const allGitlabProjects = (await gitlabApi.getAllProjects()) as GitlabProject[]; const projectRecords = await query( sqlClientPool, 'SELECT * FROM `project` WHERE name IN (:projects)', @@ -53,8 +52,7 @@ interface GitlabProject { logger.debug(`Processing ${project.name}`); const gitlabProject = R.find( - (findProject: GitlabProject) => - sanitizeGroupName(findProject.path) === project.name, + (findProject: GitlabProject) => sanitizeGroupName(findProject.path) === project.name, )(allGitlabProjects) as GitlabProject; // Load default group @@ -86,8 +84,8 @@ interface GitlabProject { '\n', R.prop('privateKey', project).toString('openssh'), ), - public: publickey['publickey'], - fingerprint: publickey['sha256fingerprint'], + public: publickey.publickey, + fingerprint: publickey.sha256fingerprint, }; } catch (err: unknown) { if (err instanceof Error) { @@ -155,10 +153,10 @@ interface GitlabProject { // Generate new keypair const genkey = await genpk(); const keyPair = { - private: genkey['privatekeypem'], - public: genkey['publickey'], - fingerprint: genkey['sha256fingerprint'], - type: genkey['type'], + private: genkey.privatekeypem, + public: genkey.publickey, + fingerprint: genkey.sha256fingerprint, + type: genkey.type, }; // Save the newly generated key diff --git a/services/api/src/index.ts b/services/api/src/index.ts index ce42f48aba..f933191b02 100644 --- a/services/api/src/index.ts +++ b/services/api/src/index.ts @@ -1,10 +1,10 @@ import 'newrelic'; import { Server } from 'http'; -import { initSendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; +import { initSendToLagoonLogs } from './commons/logs/lagoon-logger'; import { initSendToLagoonTasks, - initSendToLagoonActions -} from '@lagoon/commons/dist/tasks'; + initSendToLagoonActions, +} from './commons/tasks'; import { waitForKeycloak } from './util/waitForKeycloak'; import { envHasConfig } from './util/config'; import { logger } from './loggers/logger'; @@ -14,16 +14,14 @@ initSendToLagoonLogs(); initSendToLagoonTasks(); initSendToLagoonActions(); -const makeGracefulShutdown = (server: Server) => { - return async (signal: NodeJS.Signals) => { - logger.info(`${signal}: API Shutting Down`); +const makeGracefulShutdown = (_server: Server) => async (signal: NodeJS.Signals) => { + logger.info(`${signal}: API Shutting Down`); - logger.verbose('Closing sqlClientPool'); - const { sqlClientPool } = await import('./clients/sqlClient'); - await sqlClientPool.end(); + logger.verbose('Closing sqlClientPool'); + const { sqlClientPool } = await import('./clients/sqlClient'); + await sqlClientPool.end(); - process.kill(process.pid, signal); - }; + process.kill(process.pid, signal); }; (async () => { diff --git a/services/api/src/lib/keycloak-grant-manager.js b/services/api/src/lib/keycloak-grant-manager.js index 20f1283bdb..c47ea9eacc 100644 --- a/services/api/src/lib/keycloak-grant-manager.js +++ b/services/api/src/lib/keycloak-grant-manager.js @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict' -const URL = require('url') -const http = require('http') -const https = require('https') -const crypto = require('crypto') -const querystring = require('querystring') +const URL = require('url'); +const http = require('http'); +const https = require('https'); +const crypto = require('crypto'); +const querystring = require('querystring'); -const Grant = require('keycloak-connect/middleware/auth-utils/grant') -const Token = require('keycloak-connect/middleware/auth-utils/token') -const Rotation = require('keycloak-connect/middleware/auth-utils/rotation') +const Grant = require('keycloak-connect/middleware/auth-utils/grant'); +const Token = require('keycloak-connect/middleware/auth-utils/token'); +const Rotation = require('keycloak-connect/middleware/auth-utils/rotation'); /** * Construct a grant manager. @@ -32,16 +31,16 @@ const Rotation = require('keycloak-connect/middleware/auth-utils/rotation') * * @constructor */ -function GrantManager (config) { - this.realmUrl = config.realmUrl - this.clientId = config.clientId - this.secret = config.secret - this.publicKey = config.publicKey - this.public = config.public - this.bearerOnly = config.bearerOnly - this.notBefore = 0 - this.rotation = new Rotation(config) - this.verifyTokenAudience = config.verifyTokenAudience +function GrantManager(config) { + this.realmUrl = config.realmUrl; + this.clientId = config.clientId; + this.secret = config.secret; + this.publicKey = config.publicKey; + this.public = config.public; + this.bearerOnly = config.bearerOnly; + this.notBefore = 0; + this.rotation = new Rotation(config); + this.verifyTokenAudience = config.verifyTokenAudience; } /** @@ -58,19 +57,19 @@ function GrantManager (config) { * @param {String} password The cleartext password. * @param {Function} callback Optional callback, if not using promises. */ -GrantManager.prototype.obtainDirectly = function obtainDirectly (username, password, +GrantManager.prototype.obtainDirectly = function obtainDirectly(username, password, callback, scopeParam) { const params = { client_id: this.clientId, username, password, grant_type: 'password', - scope: scopeParam || 'openid' - } - const handler = createHandler(this) - const options = postOptions(this) - return nodeify(fetch(this, handler, options, params), callback) -} + scope: scopeParam || 'openid', + }; + const handler = createHandler(this); + const options = postOptions(this); + return nodeify(fetch(this, handler, options, params), callback); +}; /** * Obtain a grant from a previous interactive login which results in a code. @@ -90,111 +89,111 @@ GrantManager.prototype.obtainDirectly = function obtainDirectly (username, passw * @param {String} sessionHost Optional session host for targetted Keycloak console post-backs. * @param {Function} callback Optional callback, if not using promises. */ -GrantManager.prototype.obtainFromCode = function obtainFromCode (request, code, sessionId, sessionHost, callback) { +GrantManager.prototype.obtainFromCode = function obtainFromCode(request, code, sessionId, sessionHost, callback) { const params = { client_session_state: sessionId, client_session_host: sessionHost, code, grant_type: 'authorization_code', client_id: this.clientId, - redirect_uri: request.session ? request.session.auth_redirect_uri : {} - } - const handler = createHandler(this) - const options = postOptions(this) + redirect_uri: request.session ? request.session.auth_redirect_uri : {}, + }; + const handler = createHandler(this); + const options = postOptions(this); - return nodeify(fetch(this, handler, options, params), callback) -} + return nodeify(fetch(this, handler, options, params), callback); +}; -GrantManager.prototype.checkPermissions = function obtainPermissions (authzRequest, request, callback) { +GrantManager.prototype.checkPermissions = function obtainPermissions(authzRequest, request, callback) { const params = { - grant_type: 'urn:ietf:params:oauth:grant-type:uma-ticket' - } + grant_type: 'urn:ietf:params:oauth:grant-type:uma-ticket', + }; if (authzRequest.audience) { - params.audience = authzRequest.audience + params.audience = authzRequest.audience; } else { - params.audience = this.clientId + params.audience = this.clientId; } if (authzRequest.response_mode) { - params.response_mode = authzRequest.response_mode + params.response_mode = authzRequest.response_mode; } if (authzRequest.claim_token) { - params.claim_token = authzRequest.claim_token - params.claim_token_format = authzRequest.claim_token_format + params.claim_token = authzRequest.claim_token; + params.claim_token_format = authzRequest.claim_token_format; } - const options = postOptions(this) + const options = postOptions(this); if (this.public) { if (request.kauth && request.kauth.grant && request.kauth.grant.access_token) { - options.headers.Authorization = 'Bearer ' + request.kauth.grant.access_token.token + options.headers.Authorization = `Bearer ${request.kauth.grant.access_token.token}`; } } else { - const header = request.headers.authorization - let bearerToken + const header = request.headers.authorization; + let bearerToken; if (header && (header.indexOf('bearer ') === 0 || header.indexOf('Bearer ') === 0)) { - bearerToken = header.substring(7) + bearerToken = header.substring(7); } if (!bearerToken) { if (request.kauth && request.kauth.grant && request.kauth.grant.access_token) { - bearerToken = request.kauth.grant.access_token.token + bearerToken = request.kauth.grant.access_token.token; } else { - return Promise.reject(new Error('No bearer in header')) + return Promise.reject(new Error('No bearer in header')); } } - params.subject_token = bearerToken + params.subject_token = bearerToken; } - let permissions = authzRequest.permissions + let permissions = authzRequest.permissions; if (!permissions) { - permissions = [] + permissions = []; } for (let i = 0; i < permissions.length; i++) { - const resource = permissions[i] - let permission = resource.id + const resource = permissions[i]; + let permission = resource.id; if (resource.scopes && resource.scopes.length > 0) { - permission += '#' + permission += '#'; for (let j = 0; j < resource.scopes.length; j++) { - const scope = resource.scopes[j] + const scope = resource.scopes[j]; if (permission.indexOf('#') !== permission.length - 1) { - permission += ',' + permission += ','; } - permission += scope + permission += scope; } } if (!params.permission) { - params.permission = [] + params.permission = []; } - params.permission.push(permission) + params.permission.push(permission); } - const manager = this + const manager = this; const handler = (resolve, reject, json) => { try { if (authzRequest.response_mode === 'decision' || authzRequest.response_mode === 'permissions') { - callback(JSON.parse(json)) + callback(JSON.parse(json)); } else { - resolve(manager.createGrant(json)) + resolve(manager.createGrant(json)); } } catch (err) { - reject(err) + reject(err); } - } + }; - return nodeify(fetch(this, handler, options, params)) -} + return nodeify(fetch(this, handler, options, params)); +}; /** * Obtain a service account grant. @@ -204,17 +203,17 @@ GrantManager.prototype.checkPermissions = function obtainPermissions (authzReque * * @param {Function} callback Optional callback, if not using promises. */ -GrantManager.prototype.obtainFromClientCredentials = function obtainFromlientCredentials (callback, scopeParam) { +GrantManager.prototype.obtainFromClientCredentials = function obtainFromlientCredentials(callback, scopeParam) { const params = { grant_type: 'client_credentials', scope: scopeParam || 'openid', - client_id: this.clientId - } - const handler = createHandler(this) - const options = postOptions(this) + client_id: this.clientId, + }; + const handler = createHandler(this); + const options = postOptions(this); - return nodeify(fetch(this, handler, options, params), callback) -} + return nodeify(fetch(this, handler, options, params), callback); +}; /** * Ensure that a grant is *fresh*, refreshing if required & possible. @@ -233,29 +232,29 @@ GrantManager.prototype.obtainFromClientCredentials = function obtainFromlientCre * @param {Grant} grant The grant object to ensure freshness of. * @param {Function} callback Optional callback if promises are not used. */ -GrantManager.prototype.ensureFreshness = function ensureFreshness (grant, callback) { +GrantManager.prototype.ensureFreshness = function ensureFreshness(grant, callback) { if (!grant.isExpired()) { - return nodeify(Promise.resolve(grant), callback) + return nodeify(Promise.resolve(grant), callback); } if (!grant.refresh_token) { - return nodeify(Promise.reject(new Error('Unable to refresh without a refresh token')), callback) + return nodeify(Promise.reject(new Error('Unable to refresh without a refresh token')), callback); } if (grant.refresh_token.isExpired()) { - return nodeify(Promise.reject(new Error('Unable to refresh with expired refresh token')), callback) + return nodeify(Promise.reject(new Error('Unable to refresh with expired refresh token')), callback); } const params = { grant_type: 'refresh_token', refresh_token: grant.refresh_token.token, - client_id: this.clientId - } - const handler = refreshHandler(this, grant) - const options = postOptions(this) + client_id: this.clientId, + }; + const handler = refreshHandler(this, grant); + const options = postOptions(this); - return nodeify(fetch(this, handler, options, params), callback) -} + return nodeify(fetch(this, handler, options, params), callback); +}; /** * Perform live validation of an `access_token` against the Keycloak server. @@ -265,65 +264,65 @@ GrantManager.prototype.ensureFreshness = function ensureFreshness (grant, callba * * @return {boolean} `false` if the token is invalid, or the same token if valid. */ -GrantManager.prototype.validateAccessToken = function validateAccessToken (token, callback) { - let t = token +GrantManager.prototype.validateAccessToken = function validateAccessToken(token, callback) { + let t = token; if (typeof token === 'object') { - t = token.token + t = token.token; } const params = { token: t, client_secret: this.secret, - client_id: this.clientId - } - const options = postOptions(this, '/protocol/openid-connect/token/introspect') - const handler = validationHandler(this, token) + client_id: this.clientId, + }; + const options = postOptions(this, '/protocol/openid-connect/token/introspect'); + const handler = validationHandler(this, token); - return nodeify(fetch(this, handler, options, params), callback) -} + return nodeify(fetch(this, handler, options, params), callback); +}; -GrantManager.prototype.userInfo = function userInfo (token, callback) { - const url = this.realmUrl + '/protocol/openid-connect/userinfo' +GrantManager.prototype.userInfo = function userInfo(token, callback) { + const url = `${this.realmUrl}/protocol/openid-connect/userinfo`; const options = URL.parse(url); // eslint-disable-line - options.method = 'GET' + options.method = 'GET'; - let t = token - if (typeof token === 'object') t = token.token + let t = token; + if (typeof token === 'object') t = token.token; options.headers = { - Authorization: 'Bearer ' + t, + Authorization: `Bearer ${t}`, Accept: 'application/json', - 'X-Client': 'keycloak-nodejs-connect' - } + 'X-Client': 'keycloak-nodejs-connect', + }; const promise = new Promise((resolve, reject) => { const req = getProtocol(options).request(options, (response) => { if (response.statusCode < 200 || response.statusCode >= 300) { - response.destroy() - return reject(new Error('Error fetching account')) + response.destroy(); + return reject(new Error('Error fetching account')); } - let json = '' - response.on('data', (d) => (json += d.toString())) + let json = ''; + response.on('data', (d) => (json += d.toString())); response.on('end', () => { - const data = JSON.parse(json) - if (data.error) reject(data) - else resolve(data) - }) - }) - req.on('error', reject) - req.end() - }) - - return nodeify(promise, callback) -} - -GrantManager.prototype.getAccount = function getAccount () { - console.error('GrantManager#getAccount is deprecated. See GrantManager#userInfo') - return this.userInfo.apply(this, arguments) -} - -GrantManager.prototype.isGrantRefreshable = function isGrantRefreshable (grant) { - return !this.bearerOnly && (grant && grant.refresh_token) -} + const data = JSON.parse(json); + if (data.error) reject(data); + else resolve(data); + }); + }); + req.on('error', reject); + req.end(); + }); + + return nodeify(promise, callback); +}; + +GrantManager.prototype.getAccount = function getAccount() { + console.error('GrantManager#getAccount is deprecated. See GrantManager#userInfo'); + return this.userInfo.apply(this, arguments); +}; + +GrantManager.prototype.isGrantRefreshable = function isGrantRefreshable(grant) { + return !this.bearerOnly && (grant && grant.refresh_token); +}; /** * Create a `Grant` object from a string of JSON data. @@ -336,9 +335,9 @@ GrantManager.prototype.isGrantRefreshable = function isGrantRefreshable (grant) * @param {String|Object} rawData The raw JSON string received from the Keycloak server or from a client. * @return {Promise} A promise reoslving a grant. */ -GrantManager.prototype.createGrant = function createGrant (rawData) { - let grantData = rawData - if (typeof rawData !== 'object') grantData = JSON.parse(grantData) +GrantManager.prototype.createGrant = function createGrant(rawData) { + let grantData = rawData; + if (typeof rawData !== 'object') grantData = JSON.parse(grantData); const grant = new Grant({ access_token: (grantData.access_token ? new Token(grantData.access_token, this.clientId) : undefined), @@ -346,20 +345,19 @@ GrantManager.prototype.createGrant = function createGrant (rawData) { id_token: (grantData.id_token ? new Token(grantData.id_token) : undefined), expires_in: grantData.expires_in, token_type: grantData.token_type, - __raw: rawData - }) + __raw: rawData, + }); if (this.isGrantRefreshable(grant)) { return new Promise((resolve, reject) => { this.ensureFreshness(grant) .then(g => this.validateGrant(g)) .then(g => resolve(g)) - .catch(err => reject(err)) - }) - } else { - return this.validateGrant(grant) + .catch(err => reject(err)); + }); } -} + return this.validateGrant(grant); +}; /** * Validate the grant and all tokens contained therein. @@ -374,34 +372,32 @@ GrantManager.prototype.createGrant = function createGrant (rawData) { * @return {Promise} That resolves to a validated grant or * rejects with an error if any of the tokens are invalid. */ -GrantManager.prototype.validateGrant = function validateGrant (grant) { - const self = this - const validateGrantToken = (grant, tokenName, expectedType) => { - return new Promise((resolve, reject) => { +GrantManager.prototype.validateGrant = function validateGrant(grant) { + const self = this; + const validateGrantToken = (grant, tokenName, expectedType) => new Promise((resolve, reject) => { // check the access token - this.validateToken(grant[tokenName], expectedType).then(token => { - grant[tokenName] = token - resolve() - }).catch((err) => { - reject(new Error('Grant validation failed. Reason: ' + err.message)) - }) - }) - } + this.validateToken(grant[tokenName], expectedType).then(token => { + grant[tokenName] = token; + resolve(); + }).catch((err) => { + reject(new Error(`Grant validation failed. Reason: ${err.message}`)); + }); + }); return new Promise((resolve, reject) => { - const promises = [] - promises.push(validateGrantToken(grant, 'access_token', 'Bearer')) + const promises = []; + promises.push(validateGrantToken(grant, 'access_token', 'Bearer')); if (!self.bearerOnly) { if (grant.id_token) { - promises.push(validateGrantToken(grant, 'id_token', 'ID')) + promises.push(validateGrantToken(grant, 'id_token', 'ID')); } } Promise.all(promises).then(() => { - resolve(grant) + resolve(grant); }).catch((err) => { - reject(new Error(err.message)) - }) - }) -} + reject(new Error(err.message)); + }); + }); +}; /** * Validate a token. @@ -414,128 +410,124 @@ GrantManager.prototype.validateGrant = function validateGrant (grant) { * * @return {Promise} That resolve a token */ -GrantManager.prototype.validateToken = function validateToken (token, expectedType) { +GrantManager.prototype.validateToken = function validateToken(token, expectedType) { return new Promise((resolve, reject) => { if (!token) { - reject(new Error('invalid token (missing)')) + reject(new Error('invalid token (missing)')); } else if (token.isExpired()) { - reject(new Error('invalid token (expired)')) + reject(new Error('invalid token (expired)')); } else if (!token.signed) { - reject(new Error('invalid token (not signed)')) + reject(new Error('invalid token (not signed)')); } else if (token.content.typ !== expectedType) { - reject(new Error('invalid token (wrong type)')) + reject(new Error('invalid token (wrong type)')); } else if (token.content.iat < this.notBefore) { - reject(new Error('invalid token (stale token)')) + reject(new Error('invalid token (stale token)')); } else if (token.content.iss !== this.realmUrl) { - reject(new Error('invalid token (wrong ISS)')) + reject(new Error('invalid token (wrong ISS)')); } else { - const audienceData = Array.isArray(token.content.aud) ? token.content.aud : [token.content.aud] + const audienceData = Array.isArray(token.content.aud) ? token.content.aud : [token.content.aud]; if (expectedType === 'ID') { if (!audienceData.includes(this.clientId)) { - reject(new Error('invalid token (wrong audience)')) + reject(new Error('invalid token (wrong audience)')); } if (token.content.azp && token.content.azp !== this.clientId) { - reject(new Error('invalid token (authorized party should match client id)')) + reject(new Error('invalid token (authorized party should match client id)')); } } else if (this.verifyTokenAudience) { if (!audienceData.includes(this.clientId)) { - reject(new Error('invalid token (wrong audience)')) + reject(new Error('invalid token (wrong audience)')); } } - const verify = crypto.createVerify('RSA-SHA256') + const verify = crypto.createVerify('RSA-SHA256'); // if public key has been supplied use it to validate token if (this.publicKey) { try { - verify.update(token.signed) + verify.update(token.signed); if (!verify.verify(this.publicKey, token.signature, 'base64')) { - reject(new Error('invalid token (signature)')) + reject(new Error('invalid token (signature)')); } else { - resolve(token) + resolve(token); } } catch (err) { - reject(new Error('Misconfigured parameters while validating token. Check your keycloak.json file!')) + reject(new Error('Misconfigured parameters while validating token. Check your keycloak.json file!')); } } else { // retrieve public KEY and use it to validate token this.rotation.getJWK(token.header.kid).then(key => { - verify.update(token.signed) + verify.update(token.signed); if (!verify.verify(key, token.signature)) { - reject(new Error('invalid token (public key signature)')) + reject(new Error('invalid token (public key signature)')); } else { - resolve(token) + resolve(token); } }).catch((err) => { - reject(new Error('failed to load public key to verify token. Reason: ' + err.message)) - }) + reject(new Error(`failed to load public key to verify token. Reason: ${err.message}`)); + }); } } - }) -} + }); +}; -const getProtocol = (opts) => { - return opts.protocol === 'https:' ? https : http -} +const getProtocol = (opts) => opts.protocol === 'https:' ? https : http; const nodeify = (promise, cb) => { - if (typeof cb !== 'function') return promise - return promise.then((res) => cb(null, res)).catch((err) => cb(err)) -} + if (typeof cb !== 'function') return promise; + return promise.then((res) => cb(null, res)).catch((err) => cb(err)); +}; const createHandler = (manager) => (resolve, reject, json) => { try { - resolve(manager.createGrant(json)) + resolve(manager.createGrant(json)); } catch (err) { - reject(err) + reject(err); } -} +}; const refreshHandler = (manager, grant) => (resolve, reject, json) => { manager.createGrant(json) .then((grant) => resolve(grant)) - .catch((err) => reject(err)) -} + .catch((err) => reject(err)); +}; const validationHandler = (manager, token) => (resolve, reject, json) => { - const data = JSON.parse(json) - if (!data.active) resolve(false) - else resolve(token) -} + const data = JSON.parse(json); + if (!data.active) resolve(false); + else resolve(token); +}; const postOptions = (manager, path) => { - const realPath = path || '/protocol/openid-connect/token' + const realPath = path || '/protocol/openid-connect/token'; const opts = URL.parse(manager.realmUrl + realPath); // eslint-disable-line opts.headers = { 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Client': 'keycloak-nodejs-connect' - } + 'X-Client': 'keycloak-nodejs-connect', + }; if (!manager.public) { - opts.headers.Authorization = 'Basic ' + Buffer.from(manager.clientId + ':' + manager.secret).toString('base64') + opts.headers.Authorization = `Basic ${Buffer.from(`${manager.clientId}:${manager.secret}`).toString('base64')}`; } - opts.method = 'POST' - return opts -} - -const fetch = (manager, handler, options, params) => { - return new Promise((resolve, reject) => { - const data = (typeof params === 'string' ? params : querystring.stringify(params)) - options.headers['Content-Length'] = data.length + opts.method = 'POST'; + return opts; +}; + +const fetch = (manager, handler, options, params) => new Promise((resolve, reject) => { + const data = (typeof params === 'string' ? params : querystring.stringify(params)); + options.headers['Content-Length'] = data.length; + + const req = getProtocol(options).request(options, (response) => { + let json = ''; + response.on('data', (d) => (json += d.toString())); + response.on('end', () => { + if (response.statusCode < 200 || response.statusCode > 299) { + response.destroy(); + return reject(new Error(`${response.statusCode}:${http.STATUS_CODES[response.statusCode]}:${json}`)); + } + handler(resolve, reject, json); + }); + }); - const req = getProtocol(options).request(options, (response) => { - let json = '' - response.on('data', (d) => (json += d.toString())) - response.on('end', () => { - if (response.statusCode < 200 || response.statusCode > 299) { - response.destroy() - return reject(new Error(response.statusCode + ':' + http.STATUS_CODES[response.statusCode] + ':' + json)) - } - handler(resolve, reject, json) - }) - }) - - req.write(data) - req.on('error', reject) - req.end() - }) -} + req.write(data); + req.on('error', reject); + req.end(); +}); -module.exports = GrantManager +module.exports = GrantManager; diff --git a/services/api/src/loggers/lagoonLogsTransport.ts b/services/api/src/loggers/lagoonLogsTransport.ts index a0050dce06..c4533a1c1c 100644 --- a/services/api/src/loggers/lagoonLogsTransport.ts +++ b/services/api/src/loggers/lagoonLogsTransport.ts @@ -1,88 +1,89 @@ import Transport = require('winston-transport'); -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; +import { sendToLagoonLogs } from '../commons/logs/lagoon-logger'; import { parseAndCleanMeta } from './userActivityLogger'; import { Sql } from '../resources/audit/sql'; import { sqlClientPool } from '../clients/sqlClient'; import { query } from '../util/db'; import { logger } from './logger'; -import { AuditSourceType } from '@lagoon/commons/dist/types'; +import { AuditSourceType } from '../commons/types'; export class LagoonLogsTransport extends Transport { - constructor(options) { - super(options); - } + // eslint-disable-next-line no-useless-constructor + constructor(options) { + super(options); + } - private parseLagoonLogsPayload = (info) => { - let message: string = ''; - if (info.message !== undefined) { - message = info.message; - } + private parseLagoonLogsPayload = (info) => { + let message: string = ''; + if (info.message !== undefined) { + message = info.message; + } - const level = info[Symbol.for('level')] || info.level; - const formattedMessage = info[Symbol.for('message')]; + const level = info[Symbol.for('level')] || info.level; + const formattedMessage = info[Symbol.for('message')]; - const meta = parseAndCleanMeta(Object.fromEntries( - Object.entries(info).filter(([key]) => typeof key !== 'symbol'))) + const meta = parseAndCleanMeta(Object.fromEntries( + Object.entries(info).filter(([key]) => typeof key !== 'symbol'))); - return { - severity: info.level, - project: info.project ? info.project : "", - uuid: info.uuid ? info.uuid : "", - event: info.event ? info.event : "api:unknownEvent", - meta: meta ? meta : {}, - message: formattedMessage ? formattedMessage : message, - level: level ? level : "" - }; - } + return { + severity: info.level, + project: info.project ? info.project : '', + uuid: info.uuid ? info.uuid : '', + event: info.event ? info.event : 'api:unknownEvent', + meta: meta || {}, + message: formattedMessage || message, + level: level || '', + }; + }; - log(info: any, next: () => void): any { - const lagoonLogsPayload = this.parseLagoonLogsPayload(info); - setImmediate(() => this.emit('logged', lagoonLogsPayload)); + log(info: any, next: () => void): any { + const lagoonLogsPayload = this.parseLagoonLogsPayload(info); + setImmediate(() => this.emit('logged', lagoonLogsPayload)); - // USER ACTIVITY AUDIT LOGGING - // only authenticated access_token requests, not internal tokens (aka legacy tokens) - // saving internal tokens to the event log would result in massive amounts of logged data - // ideally LEGACY_EXPIRY_MAX and LEGACY_EXPIRY_REJECT are tuned to ensure use of legacy tokens is useless outside - // of internal systems - if (info.event && info.event != "api:unknownEvent" && info.payload?.resource && info.user?.access_token) { - // try and determine the user request from headers, fall back to api - const requestSource = info.headers['referer'] ? AuditSourceType.UI : info.headers['user-agent'].includes("lagoon-client") ? AuditSourceType.CLI : AuditSourceType.API - const aLog = { - usid: info.user.access_token.content.sub, - emailAddress: info.user.access_token ? info.user.access_token.content.email : info.user.iss, - resourceId: info.payload.resource?.id || null, - resourceType: info.payload.resource.type, - resourceDetails: info.payload.resource.details, - ipAddress: info.headers?.ipAddress || "null", - linkedResourceId: info.payload.linkedResource?.id || null, - linkedResourceType: info.payload.linkedResource?.type || null, - linkedResourceDetails: info.payload.linkedResource?.details || null, - impersonatorId: info.user.access_token.content.impersonator ? info.user.access_token.content.impersonator.id : null, - impersonatorUsername: info.user.access_token.content.impersonator ? info.user.access_token.content.impersonator.username : null, - source: requestSource, - auditEvent: `${info.event}`, - organizationId: info.payload?.organizationId || null, - } - try { - // save the log - query( - sqlClientPool, - Sql.insertAuditLog(aLog) - ); - } catch (err) { - logger.warn(`unable to save auditlog ${JSON.stringify(aLog)}`) - } + // USER ACTIVITY AUDIT LOGGING + // only authenticated access_token requests, not internal tokens (aka legacy tokens) + // saving internal tokens to the event log would result in massive amounts of logged data + // ideally LEGACY_EXPIRY_MAX and LEGACY_EXPIRY_REJECT are tuned to ensure use of legacy tokens is useless outside + // of internal systems + if (info.event && info.event !== 'api:unknownEvent' && info.payload?.resource && info.user?.access_token) { + // try and determine the user request from headers, fall back to api + const requestSource = info.headers?.referer ? AuditSourceType.UI : info.headers?.['user-agent']?.includes('lagoon-client') ? AuditSourceType.CLI : AuditSourceType.API; + const aLog = { + usid: info.user.access_token.content.sub, + emailAddress: info.user.access_token ? info.user.access_token.content.email : info.user.iss, + resourceId: info.payload.resource?.id || null, + resourceType: info.payload.resource.type, + resourceDetails: info.payload.resource.details, + ipAddress: info.headers?.ipAddress || 'null', + linkedResourceId: info.payload.linkedResource?.id || null, + linkedResourceType: info.payload.linkedResource?.type || null, + linkedResourceDetails: info.payload.linkedResource?.details || null, + impersonatorId: info.user.access_token.content.impersonator ? info.user.access_token.content.impersonator.id : null, + impersonatorUsername: info.user.access_token.content.impersonator ? info.user.access_token.content.impersonator.username : null, + source: requestSource, + auditEvent: `${info.event}`, + organizationId: info.payload?.organizationId || null, + }; + try { + // save the log + query( + sqlClientPool, + Sql.insertAuditLog(aLog), + ); + } catch (_err) { + logger.warn(`unable to save auditlog ${JSON.stringify(aLog)}`); } - - // all user activity logs including legacy tokens are still shipped to the lagoon-logs queue - sendToLagoonLogs( - lagoonLogsPayload.severity, - lagoonLogsPayload.project, - lagoonLogsPayload.uuid, - lagoonLogsPayload.event, - lagoonLogsPayload.meta, - lagoonLogsPayload.message - ); - next(); } + + // all user activity logs including legacy tokens are still shipped to the lagoon-logs queue + sendToLagoonLogs( + lagoonLogsPayload.severity, + lagoonLogsPayload.project, + lagoonLogsPayload.uuid, + lagoonLogsPayload.event, + lagoonLogsPayload.meta, + lagoonLogsPayload.message, + ); + next(); + } } diff --git a/services/api/src/loggers/logger.ts b/services/api/src/loggers/logger.ts index 6ca2a68ce0..92c86ec882 100644 --- a/services/api/src/loggers/logger.ts +++ b/services/api/src/loggers/logger.ts @@ -1,6 +1,9 @@ -const { addColors, createLogger, format, transports } = require('winston'); import { getConfigFromEnv } from '../util/config'; -import { levels, colors } from '@lagoon/commons/dist/logs/'; +import { levels, colors } from '../commons/logs'; + +const { + addColors, createLogger, format, transports, +} = require('winston'); addColors(colors); @@ -12,14 +15,14 @@ export const logger = createLogger({ format.splat(), format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), format.printf( - ({ timestamp, level, message }) => `[${timestamp}] [${level}]: ${message}` - ) + ({ timestamp, level, message }) => `[${timestamp}] [${level}]: ${message}`, + ), ), transports: [ new transports.Console({ level: getConfigFromEnv('CONSOLE_LOGGING_LEVEL', 'info'), handleExceptions: true, - json: false - }) - ] + json: false, + }), + ], }); diff --git a/services/api/src/loggers/userActivityLogger.ts b/services/api/src/loggers/userActivityLogger.ts index 02980e1351..b438a3c561 100644 --- a/services/api/src/loggers/userActivityLogger.ts +++ b/services/api/src/loggers/userActivityLogger.ts @@ -1,7 +1,10 @@ -import winston, { Logger } from 'winston'; -const { addColors, createLogger, format, transports } = require('winston'); +import winston from 'winston'; import { LagoonLogsTransport } from './lagoonLogsTransport'; +const { + addColors, createLogger, format, transports, +} = require('winston'); + export interface IUserActivityLogger extends winston.Logger { user_info: winston.LeveledLogMethod; user_auth: winston.LeveledLogMethod; @@ -61,7 +64,7 @@ const { colors, levels } = { user_info: 'cyan', user_auth: 'cyan', user_action: 'green', - } + }, }; addColors(colors); @@ -69,33 +72,38 @@ addColors(colors); export const parseAndCleanMeta = (meta: IMetaLogger) => { if (meta) { Object.keys(meta).map(key => { - if (meta[key] != undefined) { + if (meta[key] != null) { if (key === 'user') { - const { access_token, username, email, iat, iss, sub, aud, source, gitlabId, role, permissions } = meta[key]; + const { + access_token, username, email, iat, iss, sub, aud, source, gitlabId, role, permissions, + } = meta[key]; if (access_token) { - const { jti, email, email_verified, preferred_username, azp, typ, auth_time, iat, iss, sub, aud, realm_access, scope } = access_token.content; + const { + jti, email, email_verified, preferred_username, azp, typ, auth_time, iat, iss, sub, aud, realm_access, scope, + } = access_token.content; meta[key] = { ...(sub && { id: sub }), - ...(jti && { jti: jti }), - ...(preferred_username && { preferred_username: preferred_username }), - ...(email && { email: email }), - ...(email_verified && { email_verified: email_verified }), - ...(azp && { azp: azp }), - ...(typ && { typ: typ }), - ...(auth_time && { auth_time: auth_time }), - ...(iat && { iat: iat }), - ...(iss && { iss: iss }), - ...(scope && { scope: scope }), - ...(aud && { aud: aud }), - ...(source && { source: source }), + ...(jti && { jti }), + ...(preferred_username && { preferred_username }), + ...(email && { email }), + ...(email_verified && { email_verified }), + ...(azp && { azp }), + ...(typ && { typ }), + ...(auth_time && { auth_time }), + ...(iat && { iat }), + ...(iss && { iss }), + ...(scope && { scope }), + ...(aud && { aud }), + ...(source && { source }), ...(realm_access && { roles: realm_access.roles }), - ...(gitlabId && { gitlabId: gitlabId }) - } - } - else { + ...(gitlabId && { gitlabId }), + }; + } else { // Legacy token - meta[key] = { id: sub, username, email, role, source, iss, aud, iat, permissions } + meta[key] = { + id: sub, username, email, role, source, iss, aud, iat, permissions, + }; } } @@ -105,8 +113,12 @@ export const parseAndCleanMeta = (meta: IMetaLogger) => { } if (key === 'headers') { - const { 'user-agent': user_agent, 'accept-language': accept_language, 'content-type': content_type, 'content-length': content_length, host, origin, referer, ipAddress } = meta[key]; - meta[key] = { 'user-agent': user_agent, 'accept-language': accept_language, 'content-type': content_type, 'content-length': content_length, host, origin, referer, ipAddress } + const { + 'user-agent': user_agent, 'accept-language': accept_language, 'content-type': content_type, 'content-length': content_length, host, origin, referer, ipAddress, + } = meta[key]; + meta[key] = { + 'user-agent': user_agent, 'accept-language': accept_language, 'content-type': content_type, 'content-length': content_length, host, origin, referer, ipAddress, + }; } } }); @@ -116,22 +128,20 @@ export const parseAndCleanMeta = (meta: IMetaLogger) => { return {}; }; -export const findAndRemoveSensitiveFieldsFromNestedObj = (obj, keys) => { - return (obj !== Object(obj) - ? obj - : Array.isArray(obj) +export const findAndRemoveSensitiveFieldsFromNestedObj = (obj, keys) => (obj !== Object(obj) + ? obj + : Array.isArray(obj) ? obj.map((item) => findAndRemoveSensitiveFieldsFromNestedObj(item, keys)) : Object.keys(obj) .filter((k) => !keys.includes(k)) .reduce( (acc, x) => Object.assign(acc, { [x]: findAndRemoveSensitiveFieldsFromNestedObj(obj[x], keys) }), - {} + {}, ) - ) -}; +); const parseMessage = (info) => { - let message, level: string; + let message; let meta: IMetaLogger; if (info.message !== undefined) { message = info.message; @@ -141,17 +151,17 @@ const parseMessage = (info) => { meta = { user: { ...info.user }, headers: info.headers ? info.headers : null, - payload: info.payload ? info.payload : null + payload: info.payload ? info.payload : null, }; } - level = info.level ? info.level : 'info'; - return `[${info.timestamp}] [${level}]: ${message}: ${meta ? JSON.stringify(parseAndCleanMeta(meta)) : ''}` -} + const level: string = info.level ? info.level : 'info'; + return `[${info.timestamp}] [${level}]: ${message}: ${meta ? JSON.stringify(parseAndCleanMeta(meta)) : ''}`; +}; export const userActivityLogger: IUserActivityLogger = createLogger({ exitOnError: false, - levels: levels, + levels, format: format.combine( format.splat(), format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), @@ -161,14 +171,14 @@ export const userActivityLogger: IUserActivityLogger = createLogger({ level: 'user_action', format: format.combine( format.colorize(), - format.printf(info => parseMessage(info)) + format.printf(info => parseMessage(info)), ), handleExceptions: true, - json: true + json: true, }), new LagoonLogsTransport({ level: 'user_action', - json: true - }) - ] + json: true, + }), + ], }); diff --git a/services/api/src/migrations/lagoon/migration.ts b/services/api/src/migrations/lagoon/migration.ts index 595cc575d0..a8f55b6224 100644 --- a/services/api/src/migrations/lagoon/migration.ts +++ b/services/api/src/migrations/lagoon/migration.ts @@ -1,30 +1,28 @@ import { waitForKeycloak } from '../../util/waitForKeycloak'; -import { envHasConfig } from '../../util/config'; import { logger } from '../../loggers/logger'; -import { migrate } from '../../util/db' - +import { migrate } from '../../util/db'; (async () => { - await waitForKeycloak(); + await waitForKeycloak(); - // run any migrations that need keycloak before starting the api - try { - // run the migrations - logger.info('previous migrations:'); - const before = await migrate.migrate.list(); - for (const l of before) { - if (l.length) { - logger.info(`- ${l.name}`) - } - } - logger.info('performing migrations if required'); - // actually run the migrations - await migrate.migrate.latest(); - logger.info('migrations completed'); - } catch (e) { - logger.fatal(`Couldn't run migrations: ${e.message}`); - process.exit(1) + // run any migrations that need keycloak before starting the api + try { + // run the migrations + logger.info('previous migrations:'); + const before = await migrate.migrate.list(); + for (const l of before) { + if (l.length) { + logger.info(`- ${l.name}`); + } } + logger.info('performing migrations if required'); + // actually run the migrations + await migrate.migrate.latest(); + logger.info('migrations completed'); + } catch (e) { + logger.fatal(`Couldn't run migrations: ${e.message}`); + process.exit(1); + } - process.exit() + process.exit(); })(); diff --git a/services/api/src/migrations/lagoon/migrations/20240312000000_group_projects.ts b/services/api/src/migrations/lagoon/migrations/20240312000000_group_projects.ts index e70acd53ff..e5865e7eab 100644 --- a/services/api/src/migrations/lagoon/migrations/20240312000000_group_projects.ts +++ b/services/api/src/migrations/lagoon/migrations/20240312000000_group_projects.ts @@ -1,5 +1,5 @@ import * as R from 'ramda'; -import { logger } from '@lagoon/commons/dist/logs/local-logger'; +import { logger } from '../../../commons/logs/local-logger'; import { getKeycloakAdminClient } from '../../../clients/keycloak-admin'; import { sqlClientPool } from '../../../clients/sqlClient'; import { Group } from '../../../models/group'; @@ -23,28 +23,26 @@ export const up = async (migrate) => { }; const fgs = R.pipe( R.reduce(flattenGroups, []), - )(allGroups) + )(allGroups); // loop over the groups ignoring `role-subgroup` groups for (const fg of fgs) { - if (fg.attributes['type'] != "role-subgroup") { + if (fg.attributes.type !== 'role-subgroup') { const groupProjects = await GroupModel.getProjectsFromGroup(fg); for (const pid of groupProjects) { - logger.info(`Migrating project ${pid} and group ${fg.name}/${fg.id} to database`) + logger.info(`Migrating project ${pid} and group ${fg.name}/${fg.id} to database`); // add the project group association to the database - await Helpers(sqlClientPool).addProjectToGroup(pid, fg.id) + await Helpers(sqlClientPool).addProjectToGroup(pid, fg.id); } // if the group is in an organization if (R.prop('lagoon-organization', fg.attributes)) { // add the organization group association to the database - logger.info(`Migrating group ${fg.name}/${fg.id} in organization ${R.prop('lagoon-organization', fg.attributes)} to database`) - await Helpers(sqlClientPool).addOrganizationToGroup(parseInt(R.prop('lagoon-organization', fg.attributes)[0], 10), fg.id) + logger.info(`Migrating group ${fg.name}/${fg.id} in organization ${R.prop('lagoon-organization', fg.attributes)} to database`); + await Helpers(sqlClientPool).addOrganizationToGroup(parseInt(R.prop('lagoon-organization', fg.attributes)[0], 10), fg.id); } } } - return migrate.schema -} + return migrate.schema; +}; -export const down = async (migrate) => { - return migrate.schema -} +export const down = async (migrate) => migrate.schema; diff --git a/services/api/src/mocks.js b/services/api/src/mocks.js index 2224f5e982..364a7e41d9 100644 --- a/services/api/src/mocks.js +++ b/services/api/src/mocks.js @@ -1,5 +1,5 @@ -import { MockList } from 'graphql-tools'; -import faker from 'faker/locale/en'; +import { MockList } from '@graphql-tools/mock'; +import { faker } from '@faker-js/faker'; import { packages } from './data/mock-data'; // The mocks object is an Apollo Resolver Map where each mock function has the @@ -19,7 +19,7 @@ const addTime = (originalDate, hoursLimit) => { const date = new Date(originalDate); date.setTime( date.getTime() - + faker.random.number(hoursLimit*60*60*1000) + + faker.number.int(hoursLimit * 60 * 60 * 1000), ); return date.toISOString(); }; @@ -28,7 +28,7 @@ const addTime = (originalDate, hoursLimit) => { export const generator = (schema, min = 1, max) => { max = max || min; return Array.from({ - length: faker.random.number({ + length: faker.number.int({ min, max, }), @@ -38,7 +38,7 @@ export const generator = (schema, min = 1, max) => { entity[key] = innerGen(anySchema[key]); return entity; } - entity[key] = faker.fake(anySchema[key]); + entity[key] = faker.helpers.fake(anySchema[key]); return entity; }, {}); @@ -51,22 +51,29 @@ export const generator = (schema, min = 1, max) => { // 'scalar' and 'enum' mocks from typeDefs. // const mocks = { - Date: () => faker.date.between('2018-11-01T00:00:00', '2019-10-31T23:59:59').toISOString(), - JSON: () => ({ id: faker.random.number(), currency: 'usd' }), - SshKeyType: () => faker.random.arrayElement(['ssh_rsa', 'ssh_ed25519','ecdsa_sha2_nistp256','ecdsa_sha2_nistp384','ecdsa_sha2_nistp521']), - DeployType: () => faker.random.arrayElement(['branch', 'pullrequest', 'promote']), - EnvType: () => faker.random.arrayElement(['production', 'development']), - NotificationType: () => faker.random.arrayElement(['slack', 'rocketchat', 'microsoftteams', 'email']), - DeploymentStatusType: () => faker.random.arrayElement(['new', 'pending', 'running', 'cancelled', 'error', 'failed', 'complete']), - EnvVariableType: () => faker.random.arrayElement(['project', 'environment']), - EnvVariableScope: () => faker.random.arrayElement(['build', 'runtime', 'global', 'container_registry']), - TaskStatusType: () => faker.random.arrayElement(['active', 'succeeded', 'failed']), - RestoreStatusType: () => faker.random.arrayElement(['pending', 'successful', 'failed']), - EnvOrderType: () => faker.random.arrayElement(['name', 'updated']), - ProjectOrderType: () => faker.random.arrayElement(['name', 'created']), - ProjectAvailability: () => faker.random.arrayElement(['standard', 'high']), - GroupRole: () => faker.random.arrayElement(['guest', 'reporter', 'developer', 'maintainer', 'owner']), - Currency: () => faker.random.arrayElement(['AUD', 'EUR', 'GBP', 'USD', 'CHF', 'ZAR']), + Date: () => faker.date.between({ from: '2018-11-01T00:00:00', to: '2019-10-31T23:59:59' }).toISOString(), + JSON: () => ({ id: faker.number.int(), currency: 'usd' }), + SshKeyType: () => faker.helpers.arrayElement(['ssh_rsa', 'ssh_ed25519', 'ecdsa_sha2_nistp256', 'ecdsa_sha2_nistp384', 'ecdsa_sha2_nistp521']), + DeployType: () => faker.helpers.arrayElement(['branch', 'pullrequest', 'promote']), + EnvType: () => faker.helpers.arrayElement(['production', 'development']), + NotificationType: () => faker.helpers.arrayElement(['slack', 'rocketchat', 'microsoftteams', 'email', 'webhook']), + DeploymentStatusType: () => faker.helpers.arrayElement(['new', 'pending', 'running', 'cancelled', 'error', 'failed', 'complete']), + EnvVariableType: () => faker.helpers.arrayElement(['project', 'environment']), + EnvVariableScope: () => faker.helpers.arrayElement(['build', 'runtime', 'global', 'container_registry']), + TaskStatusType: () => faker.helpers.arrayElement(['active', 'succeeded', 'failed']), + RestoreStatusType: () => faker.helpers.arrayElement(['pending', 'successful', 'failed']), + EnvOrderType: () => faker.helpers.arrayElement(['name', 'updated']), + ProjectOrderType: () => faker.helpers.arrayElement(['name', 'created']), + ProjectAvailability: () => faker.helpers.arrayElement(['standard', 'high']), + GroupRole: () => faker.helpers.arrayElement(['guest', 'reporter', 'developer', 'maintainer', 'owner']), + OrganizationRole: () => faker.helpers.arrayElement(['OWNER', 'VIEWER']), + PlatformRole: () => faker.helpers.arrayElement(['OWNER', 'VIEWER']), + AuditSource: () => faker.helpers.arrayElement(['API', 'CLI', 'UI']), + RetentionPolicyType: () => faker.helpers.arrayElement(['HARBOR', 'HISTORY']), + HistoryRetentionType: () => faker.helpers.arrayElement(['COUNT', 'DAYS', 'MONTHS']), + RouteSource: () => faker.helpers.arrayElement(['API', 'YAML', 'AUTOGENERATED']), + RouteType: () => faker.helpers.arrayElement(['STANDARD', 'ACTIVE', 'STANDBY']), + RouteInsecure: () => faker.helpers.arrayElement(['Allow', 'Redirect', 'None']), }; // @@ -76,7 +83,7 @@ const mocks = { mocks.File = () => { const name = faker.system.commonFileName('tgz'); return { - id: faker.random.number(), + id: faker.number.int(), filename: name, download: `/download/${name}`, created: mocks.Date(), @@ -84,58 +91,58 @@ mocks.File = () => { }; mocks.SshKey = () => ({ - id: faker.random.number(), - name: `${faker.name.firstName()} SSH key`, - keyValue: faker.random.uuid(), + id: faker.number.int(), + name: `${faker.person.firstName()} SSH key`, + keyValue: faker.string.uuid(), keyType: mocks.SshKeyType(), - keyFingerprint: faker.random.uuid(), + keyFingerprint: faker.string.uuid(), created: mocks.Date(), }); -mocks.User = (parent, args = {}, context, info) => { - const firstName = faker.name.firstName(); - const lastName = faker.name.lastName(); +mocks.User = (parent, args = {}, _context, _info) => { + const firstName = faker.person.firstName(); + const lastName = faker.person.lastName(); const user = { - id: faker.random.number(), - email: faker.internet.email(firstName, lastName), + id: faker.number.int(), + email: faker.internet.email({ firstName, lastName }), firstName, lastName, comment: faker.lorem.words(5), - gitlabId: faker.random.number(), - sshKeys: [ mocks.SshKey() ], + gitlabId: faker.number.int(), + sshKeys: [mocks.SshKey()], groups: [], }; return { ...user, groups: args.hasOwnProperty('groups') ? args.groups - : [mocks.Group(null, {user})], - } + : [mocks.Group(null, { user })], + }; }; -mocks.GroupMembership = (parent, args = {}, context, info) => ({ +mocks.GroupMembership = (parent, args = {}, _context, _info) => ({ user: args.hasOwnProperty('user') ? args.user - : mocks.User(null, {groups: []}), + : mocks.User(null, { groups: [] }), role: mocks.GroupRole(), }); -mocks.Group = (parent, args = {}, context, info) => { +mocks.Group = (parent, args = {}, _context, _info) => { const user = args.hasOwnProperty('user') ? args.user - : mocks.User(null, {groups: []}); - const user2 = mocks.User(null, {groups: []}); + : mocks.User(null, { groups: [] }); + const user2 = mocks.User(null, { groups: [] }); const group = { - id: faker.random.number(), - name: faker.random.word(), + id: faker.number.int(), + name: faker.word.sample(), // Only mock nested groups when not asked for a user-specific group. groups: !args.hasOwnProperty('user') - ? [mocks.Group(null, {user: mocks.User(null, {groups: []})})] + ? [mocks.Group(null, { user: mocks.User(null, { groups: [] }) })] : [], members: [ - mocks.GroupMembership(null, {user}), - mocks.GroupMembership(null, {user: user2}), + mocks.GroupMembership(null, { user }), + mocks.GroupMembership(null, { user: user2 }), ], }; @@ -146,70 +153,156 @@ mocks.Group = (parent, args = {}, context, info) => { return group; }; -//mocks.Group = mocks.GroupInterface; +// mocks.Group = mocks.GroupInterface; -mocks.Me = () => ({ - id: faker.random.number(), - firstName: faker.name.firstName(), - lastName: faker.name.lastName(), +mocks.OrgUser = () => ({ + id: faker.string.uuid(), email: faker.internet.email(), - username: faker.internet.userName(), - sshKeys: [{ - id: faker.random.number(), - name: faker.random.arrayElement(['key-1', 'key-2', 'key-3']), - keyType: faker.random.arrayElement(['SSH_RSA', 'SSH_ED25519', 'ECDSA_SHA2_NISTP256', 'ECDSA_SHA2_NISTP384', 'ECDSA_SHA2_NISTP521']), - created: mocks.Date(), - keyFingerprint: faker.random.uuid() - }, - { - id: faker.random.number(), - name: faker.random.arrayElement(['key-1', 'key-2', 'key-3']), - keyType: faker.random.arrayElement(['SSH_RSA', 'SSH_ED25519', 'ECDSA_SHA2_NISTP256', 'ECDSA_SHA2_NISTP384', 'ECDSA_SHA2_NISTP521']), - created: mocks.Date(), - keyFingerprint: faker.random.uuid() - }] -}) + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + comment: faker.lorem.words(3), + organizationRole: mocks.OrganizationRole(), + has2faEnabled: faker.helpers.arrayElement([true, false]), + isFederatedUser: false, +}); + +mocks.OrgGroup = () => ({ + id: faker.string.uuid(), + name: faker.word.sample(), + type: 'group', + groups: [], + members: [], + memberCount: faker.number.int({ min: 1, max: 20 }), + projects: [], + organization: faker.number.int({ min: 1, max: 10 }), +}); + +mocks.OrgProject = () => ({ + id: faker.number.int(), + name: `${faker.company.catchPhraseAdjective()} ${faker.company.bsNoun()}`.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(), + organization: faker.number.int({ min: 1, max: 10 }), + gitUrl: `git@example.com/org/${faker.word.sample()}.git`, + groups: [], + groupCount: faker.number.int({ min: 0, max: 5 }), + notifications: [], + retentionPolicies: [], + metadata: {}, +}); + +mocks.OrgEnvironment = () => ({ + id: faker.number.int(), + name: faker.helpers.arrayElement(['main', 'staging', 'development']), + project: mocks.OrgProject(), + deployType: 'branch', + deployHeadRef: 'main', + deployTitle: 'main', + autoIdle: 1, + environmentType: 'production', + openshiftProjectName: 'org-project-main', + kubernetesNamespaceName: 'org-project-main', + updated: mocks.Date(), + created: mocks.Date(), + deleted: '0000-00-00 00:00:00', + route: 'https://org-project.example.com', + routes: 'https://org-project.example.com', + services: [mocks.EnvironmentService()], + openshift: mocks.Openshift(), + kubernetes: mocks.Kubernetes(), +}); + +mocks.OrganizationNotification = () => ({ + name: 'org-notification', + type: mocks.NotificationType(), +}); + +mocks.Organization = () => ({ + id: faker.number.int(), + name: `${faker.word.sample()}-org`, + friendlyName: faker.company.name(), + description: faker.lorem.sentence(), + quotaProject: faker.number.int({ min: 5, max: 50 }), + quotaGroup: faker.number.int({ min: 5, max: 20 }), + quotaNotification: faker.number.int({ min: 5, max: 20 }), + quotaEnvironment: faker.number.int({ min: 10, max: 100 }), + quotaRoute: faker.number.int({ min: 10, max: 100 }), + deployTargets: [mocks.Openshift()], + projects: [mocks.OrgProject(), mocks.OrgProject()], + environments: [], + groups: [mocks.OrgGroup()], + owners: [mocks.OrgUser()], + notifications: [mocks.Notification()], + created: mocks.Date(), + envVariables: [], + retentionPolicies: [], +}); + +// Note: `me` query returns a User type – no separate mocks.Me needed mocks.Openshift = () => ({ - id: faker.random.number(), + id: faker.number.int(), name: 'ci-local', consoleUrl: 'https://192.168.99.100:8443/', - token: faker.random.uuid(), + token: faker.string.uuid(), routerPattern: '${project}.${branch}.192.168.99.100.xip.io', projectUser: faker.internet.userName(), sshHost: '192.168.99.1', sshPort: '22', created: mocks.Date(), + friendlyName: 'Local CI', + cloudProvider: faker.helpers.arrayElement(['aws', 'gcp', 'azure']), + cloudRegion: faker.helpers.arrayElement(['us-east-1', 'eu-west-1', 'ap-southeast-2']), +}); + +mocks.Kubernetes = () => ({ + id: faker.number.int(), + name: `${faker.word.sample()}-k8s`, + consoleUrl: 'https://192.168.99.100:6443/', + token: faker.string.uuid(), + routerPattern: '${project}.${branch}.192.168.99.100.xip.io', + sshHost: '192.168.99.1', + sshPort: '22', + created: mocks.Date(), + friendlyName: 'Local K8s', + cloudProvider: faker.helpers.arrayElement(['aws', 'gcp', 'azure']), + cloudRegion: faker.helpers.arrayElement(['us-east-1', 'eu-west-1', 'ap-southeast-2']), }); mocks.NotificationMicrosoftTeams = () => ({ - id: faker.random.number(), + id: faker.number.int(), name: 'amazeeio--lagoon-local-ci', webhook: 'https://amazeeio.teams.microsoft.com/hooks/ikF5XMohDZK7KpsZf/c9BFBt2ch8oMMuycoERJQMSLTPo8nmZhg2Hf2ny68ZpuD4Kn', }); mocks.NotificationRocketChat = () => ({ - id: faker.random.number(), + id: faker.number.int(), name: 'amazeeio--lagoon-local-ci', webhook: 'https://amazeeio.rocket.chat/hooks/ikF5XMohDZK7KpsZf/c9BFBt2ch8oMMuycoERJQMSLTPo8nmZhg2Hf2ny68ZpuD4Kn', - channel: 'lagoon-local-ci' + channel: 'lagoon-local-ci', }); mocks.NotificationSlack = () => ({ - id: faker.random.number(), + id: faker.number.int(), name: 'amazeeio--lagoon-local-ci', webhook: 'https://amazeeio.slack.com/hooks/ikF5XMohDZK7KpsZf/c9BFBt2ch8oMMuycoERJQMSLTPo8nmZhg2Hf2ny68ZpuD4Kn', - channel: 'lagoon-local-ci' + channel: 'lagoon-local-ci', }); mocks.NotificationEmail = () => ({ - id: faker.random.number(), + id: faker.number.int(), name: 'amazeeio--lagoon-local-ci', emailAddress: faker.internet.email(), }); +mocks.NotificationWebhook = () => ({ + id: faker.number.int(), + name: 'amazeeio--lagoon-local-ci', + webhook: 'https://amazeeio.example.com/hooks/ikF5XMohDZK7KpsZf/c9BFBt2ch8oMMuycoERJQMSLTPo8nmZhg2Hf2ny68ZpuD4Kn', + contentType: 'application/json', + notificationSeverityThreshold: faker.helpers.arrayElement(['NONE', 'UNKNOWN', 'NEGLIGIBLE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL']), +}); + mocks.UnassignedNotification = () => ({ - id: faker.random.number(), + id: faker.number.int(), name: 'unassigned--local-ci', type: mocks.NotificationType(), }); @@ -224,33 +317,35 @@ mocks.Notification = () => { return mocks.NotificationRocketChat(); case 'slack': return mocks.NotificationSlack(); + case 'webhook': + return mocks.NotificationWebhook(); } }; -mocks.Project = (parent, args = {}, context, info) => { +mocks.Project = (parent, args = {}, _context, _info) => { const name = `${faker.company.catchPhraseAdjective()} ${faker.company.bsNoun()}`.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); const project = { - id: faker.random.number(), + id: faker.number.int(), name, - gitUrl: faker.random.arrayElement([`git@example.com/${name}/${faker.company.bsNoun()}.git`, `https://example.com/${name}/${faker.company.bsNoun()}.git`]), + gitUrl: faker.helpers.arrayElement([`git@example.com/${name}/${faker.company.bsNoun()}.git`, `https://example.com/${name}/${faker.company.bsNoun()}.git`]), availability: mocks.ProjectAvailability(), privateKey: `-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEA+o[...]P0yoL8BoQQG2jCvYfWh6vyglQdrDYx/o6/8ecTwXokKKh6fg1q -----END RSA PRIVATE KEY-----`, subfolder: '', notifications: [mocks.Notification()], - branches: faker.random.arrayElement(['true', 'false', '^(master|staging)$']), - pullrequests: faker.random.arrayElement(['true', 'false', '[BUILD]']), + branches: faker.helpers.arrayElement(['true', 'false', '^(master|staging)$']), + pullrequests: faker.helpers.arrayElement(['true', 'false', '[BUILD]']), productionEnvironment: 'master', - autoIdle: faker.random.arrayElement([0, 1]), - storageCalc: faker.random.arrayElement([0, 1]), + autoIdle: faker.helpers.arrayElement([0, 1]), + storageCalc: faker.helpers.arrayElement([0, 1]), problemsUi: 1, factsUi: 1, openshift: mocks.Openshift(), developmentEnvironmentsLimit: 10, environments: [], created: mocks.Date(), - envVariables: [ mocks.EnvKeyValue() ], + envVariables: [mocks.EnvKeyValue()], // groups: [ mocks.Group() ], }; return { @@ -259,11 +354,12 @@ MIIJKQIBAAKCAgEA+o[...]P0yoL8BoQQG2jCvYfWh6vyglQdrDYx/o6/8ecTwXokKKh6fg1q }; }; -mocks.Environment = (parent, args = {}, context, info) => { +mocks.Environment = (parent, args = {}, _context, _info) => { const name = args.hasOwnProperty('name') ? args.name - : faker.random.arrayElement(['master', 'staging', 'development', 'pr-42', 'pr-100', 'pr-175']); - let deployType, deployBaseRef, deployHeadRef; + : faker.helpers.arrayElement(['master', 'staging', 'development', 'pr-42', 'pr-100', 'pr-175']); + let deployType; let deployBaseRef; let + deployHeadRef; if (/^pr\-/.test(name)) { deployType = 'pullrequest'; deployBaseRef = 'target'; @@ -279,14 +375,14 @@ mocks.Environment = (parent, args = {}, context, info) => { const project = args.hasOwnProperty('project') ? args.project : mocks.Project(null, { environment: {} }); const environment = { - id: faker.random.number(), + id: faker.number.int(), name, project, deployType, deployBaseRef, deployHeadRef, deployTitle: name, - autoIdle: faker.random.arrayElement([0, 1]), + autoIdle: faker.helpers.arrayElement([0, 1]), environmentType: name === 'master' ? 'production' : 'development', openshiftProjectName: `${project.name}-${name}`.toLowerCase(), updated, @@ -296,59 +392,69 @@ mocks.Environment = (parent, args = {}, context, info) => { storages: [], storageMonth: mocks.EnvironmentStorageMonth(), hitsMonth: mocks.EnvironmentHitsMonth(), - envVariables: [ mocks.EnvKeyValue() ], + envVariables: [mocks.EnvKeyValue()], route: name === 'master' ? `https://${project.name}.org` : `https://${name}.${project.name}.org`, routes: `https://${project.name}.org,https://varnish-${project.name}-org-prod.us.amazee.io,https://nginx-${project.name}-org-prod.us.amazee.io`, monitoringUrls: null, deployments: [ - mocks.Deployment(null, {environment: { created: new Date(mocks.Date()) }}), - mocks.Deployment(null, {environment: {}}) + mocks.Deployment(null, { environment: { created: new Date(mocks.Date()) } }), + mocks.Deployment(null, { environment: {} }), ], backups: [], tasks: [], problems: [], facts: [], - services: [ mocks.EnvironmentService(), mocks.EnvironmentService(), mocks.EnvironmentService() ], + services: [mocks.EnvironmentService(), mocks.EnvironmentService(), mocks.EnvironmentService()], }; return { ...environment, - storages: [ mocks.EnvironmentStorage(null, {environment}) ], + storages: [mocks.EnvironmentStorage(null, { environment })], deployments: args.hasOwnProperty('deployments') ? args.deployments : [ - mocks.Deployment(null, {environment} ), - mocks.Deployment(null, {environment} ) + mocks.Deployment(null, { environment }), + mocks.Deployment(null, { environment }), ], problems: args.hasOwnProperty('problems') ? args.problems : [ - mocks.Problem(null, {source: "Drutiny", severity: "CRITICAL"}), - mocks.Problem(null, {source: "Trivy", severity: "MEDIUM"}), - mocks.Problem(null, {source: "Drutiny", severity: "HIGH"}), - mocks.Problem(null, {source: "OWASP ZAP", severity: "LOW"}), + mocks.Problem(null, { source: 'Drutiny', severity: 'CRITICAL' }), + mocks.Problem(null, { source: 'Trivy', severity: 'MEDIUM' }), + mocks.Problem(null, { source: 'Drutiny', severity: 'HIGH' }), + mocks.Problem(null, { source: 'OWASP ZAP', severity: 'LOW' }), + mocks.Problem(), mocks.Problem(), - mocks.Problem() ], facts: args.hasOwnProperty('facts') ? args.facts : [ - mocks.Fact(null, { name: "php-version", value: faker.random.arrayElement(['7.4.1', '7.2.1', '7.1.1', '8.1.0']), source: "php-version", category: "Programming language", keyFact: true} ), - mocks.Fact(null, { name: "drupal-core", value: "9.0.12", source: "drupal-version", category: "Framework", keyFact: true} ), - mocks.Fact(null, { name: "Lagoon", value: " 21.3.0", source: "env", category: "Platform", keyFact: true} ), - mocks.Fact(null, { name: "site-code-status", value: faker.random.arrayElement(['200', '404', '403', '301', '504', '503']), source: "curl", category: "Performance", keyFact: true} ), - mocks.Fact(null, { name: "lagoon-category", value: "saas", source: "saas", category: "Lagoon", keyFact: true} ), + mocks.Fact(null, { + name: 'php-version', value: faker.helpers.arrayElement(['7.4.1', '7.2.1', '7.1.1', '8.1.0']), source: 'php-version', category: 'Programming language', keyFact: true, + }), + mocks.Fact(null, { + name: 'drupal-core', value: '9.0.12', source: 'drupal-version', category: 'Framework', keyFact: true, + }), + mocks.Fact(null, { + name: 'Lagoon', value: ' 21.3.0', source: 'env', category: 'Platform', keyFact: true, + }), + mocks.Fact(null, { + name: 'site-code-status', value: faker.helpers.arrayElement(['200', '404', '403', '301', '504', '503']), source: 'curl', category: 'Performance', keyFact: true, + }), + mocks.Fact(null, { + name: 'lagoon-category', value: 'saas', source: 'saas', category: 'Lagoon', keyFact: true, + }), + mocks.Fact(), mocks.Fact(), - mocks.Fact() ], - backups: args.hasOwnProperty('backups') ? args.backups : [ mocks.Backup(null, {environment}) ], - tasks: args.hasOwnProperty('tasks') ? args.tasks : [ mocks.Task(null, {environment}) ], + backups: args.hasOwnProperty('backups') ? args.backups : [mocks.Backup(null, { environment })], + tasks: args.hasOwnProperty('tasks') ? args.tasks : [mocks.Task(null, { environment })], }; }; mocks.EnvironmentHitsMonth = () => ({ - total: faker.random.number(), + total: faker.number.int(), }); -mocks.EnvironmentStorage = (parent, args = {}, context, info) => ({ - id: faker.random.number(), +mocks.EnvironmentStorage = (parent, args = {}, _context, _info) => ({ + id: faker.number.int(), environment: args.hasOwnProperty('environment') ? { id: args.environment.id } : { id: mocks.Environment().id }, persistentStorageClaim: '', - kibUsed: faker.random.number({ precision: 0.001 }), // Float + kibUsed: faker.number.float({ multipleOf: 0.001 }), // Float updated: mocks.Date(), }); @@ -356,7 +462,7 @@ mocks.EnvironmentStorageMonth = () => { const date = new Date(mocks.Date()); return { month: `${date.getFullYear()}-${date.getMonth() + 1}`, - kibUsed: faker.random.number({ precision: 0.001 }), + kibUsed: faker.number.float({ multipleOf: 0.001 }), }; }; @@ -364,68 +470,68 @@ mocks.EnvironmentHoursMonth = () => { const date = new Date(mocks.Date()); return { month: `${date.getFullYear()}-${date.getMonth() + 1}`, - hours: faker.random.number({ max: 24*30 }), + hours: faker.number.int({ max: 24 * 30 }), }; }; mocks.EnvironmentService = () => ({ - id: faker.random.number(), - name: faker.random.arrayElement(['cli', 'nginx', 'mariadb', 'solr', 'varnish', 'php']), + id: faker.number.int(), + name: faker.helpers.arrayElement(['cli', 'nginx', 'mariadb', 'solr', 'varnish', 'php']), }); -mocks.Fact = (parent, args = {}, context, info) => { - const id = `${faker.random.number({min: 0, max: 99999})}`; - const name = args.hasOwnProperty('name') ? args.name : faker.random.arrayElement(['drupal-version', 'drush-version', 'admin_toolbar', 'drupal-core', 'laravel', 'composer-version']); - const value = args.hasOwnProperty('value') ? args.value : faker.random.arrayElement(['8.0.1', '9.0.1', '3.2.1', '10.20.2', '4.3.4']); - const source = args.hasOwnProperty('source') ? args.source : faker.random.arrayElement(['drush_pml', 'drush_status', 'http_header', 'php-version', 'env']); - const category = args.hasOwnProperty('category') ? args.category : faker.random.arrayElement(['Application', 'Framework', 'Docker configuration', 'Drupal configuration', 'Lagoon', 'Platform', 'Programming language']); +mocks.Fact = (parent, args = {}, _context, _info) => { + const id = `${faker.number.int({ min: 0, max: 99999 })}`; + const name = args.hasOwnProperty('name') ? args.name : faker.helpers.arrayElement(['drupal-version', 'drush-version', 'admin_toolbar', 'drupal-core', 'laravel', 'composer-version']); + const value = args.hasOwnProperty('value') ? args.value : faker.helpers.arrayElement(['8.0.1', '9.0.1', '3.2.1', '10.20.2', '4.3.4']); + const source = args.hasOwnProperty('source') ? args.source : faker.helpers.arrayElement(['drush_pml', 'drush_status', 'http_header', 'php-version', 'env']); + const category = args.hasOwnProperty('category') ? args.category : faker.helpers.arrayElement(['Application', 'Framework', 'Docker configuration', 'Drupal configuration', 'Lagoon', 'Platform', 'Programming language']); const description = faker.lorem.paragraph(); - const environment = `${faker.random.number({min:0, max:5})}`; + const environment = `${faker.number.int({ min: 0, max: 5 })}`; const keyFact = args.hasOwnProperty('keyFact') ? args.keyFact : false; const type = args.hasOwnProperty('type') ? args.type : 'TEXT'; const references = [{ - id: faker.random.number(), - fid: faker.random.number(), - name: faker.random.arrayElement([ - ['nginx.company.amazee.io'], ['cli'], ['solr'], ['php'], ['backend.company.amazee.io'] - ]) + id: faker.number.int(), + fid: faker.number.int(), + name: faker.helpers.arrayElement([ + ['nginx.company.amazee.io'], ['cli'], ['solr'], ['php'], ['backend.company.amazee.io'], + ]), }]; return { - id: id, - name: name, - value: value, - source: source, - category: category, + id, + name, + value, + source, + category, description, environment, keyFact, type, - references + references, }; }; -mocks.Backup = (parent, args = {}, context, info) => { +mocks.Backup = (parent, args = {}, _context, _info) => { const date = mocks.Date(); - const backupId = faker.random.uuid(); + const backupId = faker.string.uuid(); return { - id: faker.random.number(), + id: faker.number.int(), environment: args.hasOwnProperty('environment') ? { id: args.environment.id } : { id: mocks.Environment().id }, - source: faker.random.arrayElement(['files', 'mariadb']), + source: faker.helpers.arrayElement(['files', 'mariadb']), backupId, created: date, deleted: addTime(date, 24), - restore: mocks.Restore(null, {backupId}), + restore: mocks.Restore(null, { backupId }), }; }; -mocks.Restore = (parent, args = {}, context, info) => { +mocks.Restore = (parent, args = {}, _context, _info) => { const backupId = args.hasOwnProperty('backupId') ? args.backupId - : faker.random.number(); + : faker.number.int(); return { - id: faker.random.number(), + id: faker.number.int(), backupId, status: mocks.RestoreStatusType(), restoreLocation: `/restore/${backupId}`, @@ -433,11 +539,11 @@ mocks.Restore = (parent, args = {}, context, info) => { }; }; -mocks.Deployment = (parent, args = {}, context, info) => { - const id = faker.random.number(); +mocks.Deployment = (parent, args = {}, _context, _info) => { + const id = faker.number.int(); const created = args.hasOwnProperty('created') ? args.created : mocks.Date(); - const started = addTime(created, .5); - const completed = addTime(started, .75); + const started = addTime(created, 0.5); + const completed = addTime(started, 0.75); return { id, @@ -446,240 +552,393 @@ mocks.Deployment = (parent, args = {}, context, info) => { created, started, completed, - environment: args.hasOwnProperty('environment') ? args.environment : mocks.Environment(null, {deployments: []} ), - remoteId: faker.random.number(), + environment: args.hasOwnProperty('environment') ? args.environment : mocks.Environment(null, { deployments: [] }), + remoteId: faker.number.int(), buildLog: 'Buildem logem ipsum.', }; }; mocks.EnvKeyValue = () => ({ - id: faker.random.number(), + id: faker.number.int(), scope: mocks.EnvVariableScope(), name: 'LAGOON_API_VARIABLE_PROJECT', value: '4A65DC68F2', }); -mocks.Task = (parent, args = {}, context, info) => { - const name = faker.random.arrayElement(['Site Status', 'Drupal Archive']); +mocks.Task = (parent, args = {}, _context, _info) => { + const name = faker.helpers.arrayElement(['Site Status', 'Drupal Archive']); const created = mocks.Date(); const started = addTime(created, 0.125); const completed = addTime(created, 1.7); return { - id: args.hasOwnProperty('id') ? args.id : faker.random.number(), + id: args.hasOwnProperty('id') ? args.id : faker.number.int(), name, status: mocks.TaskStatusType(), created, started, completed, - environment: args.hasOwnProperty('environment') ? args.environment : mocks.Environment(), + environment: args.hasOwnProperty('environment') ? args.environment : mocks.Environment(), service: 'cli', command: name === 'Site Status' ? 'drush status' : 'drush archive-dump', - remoteId: faker.random.uuid(), + remoteId: faker.string.uuid(), logs: 'Taskem logem ipsum.', - files: [ mocks.File() ], + files: [mocks.File()], }; }; -mocks.ProblemIdentifier = () => { - const recentYear = faker.random.arrayElement(['2019', '2020']); - const vuln_id = `CVE-${recentYear}-${faker.random.number({min: 1000, max: 99999})}`; - const severity = faker.random.arrayElement(['UNKNOWN', 'NEGLIGIBLE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL']); - const source = faker.random.arrayElement(['Harbor', 'Drutiny']); +mocks.DeployTargetConfig = () => ({ + id: faker.number.int(), + project: null, // avoid circular; set at call site if needed + weight: faker.number.int({ min: 1, max: 100 }), + branches: faker.helpers.arrayElement(['true', 'false', '^(main|master)$']), + pullrequests: faker.helpers.arrayElement(['true', 'false']), + deployTarget: mocks.Openshift(), +}); - return { - identifier: vuln_id, - severity: severity, - source: source, - problems: Array.from({ - length: faker.random.number({ - min: 1, - max: 20, - }), - }).map(() => { - return mocks.Problem() - }) - } -}; +mocks.AuditLog = () => ({ + id: faker.number.int(), + usid: faker.string.uuid(), + emailAddress: faker.internet.email(), + resourceId: `${faker.number.int({ min: 1, max: 9999 })}`, + resourceType: faker.helpers.arrayElement(['PROJECT', 'ENVIRONMENT', 'GROUP', 'USER', 'ORGANIZATION']), + resourceDetails: faker.lorem.words(3), + linkedResourceId: null, + linkedResourceType: null, + linkedResourceDetails: null, + auditEvent: faker.helpers.arrayElement(['addProject', 'deleteEnvironment', 'addUser', 'updateProject']), + impersonatorId: null, + impersonatorUsername: null, + ipAddress: faker.internet.ipv4(), + source: mocks.AuditSource(), + created: mocks.Date(), + organizationId: faker.number.int({ min: 1, max: 10 }), +}); -mocks.Problem = (parent, args = {}, context, info) => { - const recentYear = faker.random.arrayElement(['2019', '2020']); - const vuln_id = `CVE-${recentYear}-${faker.random.number({min: 1000, max: 99999})}`; - const source = faker.random.arrayElement(['Lighthouse', 'Drutiny', 'Trivy', 'OWASP ZAP', 'Script']); - // const created = faker.date.between('2019-10-01 00:00:00', '2020-03-31 23:59:59').toUTCString(); - const associatedPackage = faker.random.arrayElement(packages); - // const version = `${faker.random.number(4)}.${faker.random.number(9)}.${faker.random.number(49)}`; - // const fixedVersion = `${version}+deb8u${faker.random.number(9)}`; - const severity = faker.random.arrayElement(['UNKNOWN', 'NEGLIGIBLE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL']); - const description = faker.lorem.paragraph(); - const links = `https://security-tracker.debian.org/tracker/${vuln_id}`; - const severityScore = `0.${faker.random.number({min:1, max:9})}`; - const data = JSON.stringify({id: `${faker.random.number({min:1, max:100})}`}, null, '\t'); - - return { - identifier: vuln_id, - severity: args.hasOwnProperty('severity') ? args.severity : severity, - source: args.hasOwnProperty('source') ? args.source : source, - severityScore: severityScore, - associatedPackage: associatedPackage, - description, - links, - data - }; -}; +mocks.Route = () => ({ + id: faker.number.int(), + domain: `${faker.word.sample()}.${faker.internet.domainSuffix()}`, + service: faker.helpers.arrayElement(['nginx', 'varnish', 'node']), + tlsAcme: faker.helpers.arrayElement([true, false]), + insecure: mocks.RouteInsecure(), + primary: faker.helpers.arrayElement([true, false]), + source: mocks.RouteSource(), + type: mocks.RouteType(), + annotations: [], + pathRoutes: [], + alternativeNames: [], + updated: mocks.Date(), + created: mocks.Date(), +}); + +mocks.AdvancedTaskDefinitionArgument = () => ({ + id: faker.number.int(), + name: faker.word.sample(), + displayName: faker.word.sample(), + type: faker.helpers.arrayElement(['STRING', 'ENVIRONMENT_SOURCE_NAME', 'ENVIRONMENT_SOURCE_NAME_EXCLUDE_SELF']), + defaultValue: '', + optional: faker.helpers.arrayElement([true, false]), + range: [], +}); + +mocks.AdvancedTaskDefinitionCommand = () => ({ + id: faker.number.int(), + name: `${faker.word.sample()}-task`, + description: faker.lorem.sentence(), + confirmationText: '', + type: 'COMMAND', + service: 'cli', + command: `drush ${faker.word.sample()}`, + groupName: '', + environment: null, + project: null, + systemWide: false, + permission: faker.helpers.arrayElement(['PRODUCTION', 'DEVELOPMENT', 'GUEST']), + deployTokenInjection: false, + projectKeyInjection: false, + adminOnlyView: false, + advancedTaskDefinitionArguments: [], + created: mocks.Date(), + deleted: '0000-00-00 00:00:00', +}); + +mocks.AdvancedTaskDefinitionImage = () => ({ + id: faker.number.int(), + name: `${faker.word.sample()}-image-task`, + description: faker.lorem.sentence(), + confirmationText: '', + type: 'IMAGE', + image: `uselagoon/${faker.word.sample()}:latest`, + service: 'cli', + groupName: '', + environment: null, + project: null, + systemWide: false, + permission: faker.helpers.arrayElement(['PRODUCTION', 'DEVELOPMENT', 'GUEST']), + deployTokenInjection: false, + projectKeyInjection: false, + adminOnlyView: false, + advancedTaskDefinitionArguments: [mocks.AdvancedTaskDefinitionArgument()], + created: mocks.Date(), + deleted: '0000-00-00 00:00:00', +}); + +mocks.HarborRetentionPolicy = () => ({ + id: faker.number.int(), + name: `${faker.word.sample()}-harbor-policy`, + configuration: { + enabled: true, + rules: [{ name: 'retain-last-10', pattern: '**', latestPulled: 10 }], + schedule: '0 0 * * *', + }, + created: mocks.Date(), + updated: mocks.Date(), + source: faker.helpers.arrayElement(['project', 'organization']), +}); + +mocks.HistoryRetentionPolicy = () => ({ + id: faker.number.int(), + name: `${faker.word.sample()}-history-policy`, + configuration: { + enabled: true, + deploymentHistory: 10, + deploymentType: 'COUNT', + taskHistory: 5, + taskType: 'COUNT', + }, + created: mocks.Date(), + updated: mocks.Date(), + source: faker.helpers.arrayElement(['project', 'organization']), +}); -mocks.ProblemMutation = (schema) => { - return Array.from({ - length: faker.random.number({ - min: 1, - max: 500, - }), - }).map(() => { - let temp = schema(); - return ( - `problem${faker.random.number(1000000)}: addProblem(input: ${JSON.stringify(temp, 2, null)}) { identifier }` - ); - }); +mocks.Problem = (parent, args = {}, _context, _info) => { + const recentYear = faker.helpers.arrayElement(['2019', '2020']); + const vuln_id = `CVE-${recentYear}-${faker.number.int({ min: 1000, max: 99999 })}`; + const source = faker.helpers.arrayElement(['Lighthouse', 'Drutiny', 'Trivy', 'OWASP ZAP', 'Script']); + // const created = faker.date.between('2019-10-01 00:00:00', '2020-03-31 23:59:59').toUTCString(); + const associatedPackage = faker.helpers.arrayElement(packages); + // const version = `${faker.number.int(4)}.${faker.number.int(9)}.${faker.number.int(49)}`; + // const fixedVersion = `${version}+deb8u${faker.number.int(9)}`; + const severity = faker.helpers.arrayElement(['UNKNOWN', 'NEGLIGIBLE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL']); + const description = faker.lorem.paragraph(); + const links = `https://security-tracker.debian.org/tracker/${vuln_id}`; + const severityScore = `0.${faker.number.int({ min: 1, max: 9 })}`; + const data = JSON.stringify({ id: `${faker.number.int({ min: 1, max: 100 })}` }, null, '\t'); + + return { + identifier: vuln_id, + severity: args.hasOwnProperty('severity') ? args.severity : severity, + source: args.hasOwnProperty('source') ? args.source : source, + severityScore, + associatedPackage, + description, + links, + data, + }; }; // // Query 'type' mock from typeDefs. // mocks.Query = () => ({ - me: () => mocks.Me(), + // User / Auth + me: () => mocks.User(null, { groups: [] }), userBySshKey: () => mocks.User(), + userByEmail: () => mocks.User(), + userBySshFingerprint: () => mocks.User(), + userByEmailAndOrganization: () => mocks.OrgUser(), + userCanSshToEnvironment: () => true, + usersByOrganization: () => new MockList(3, () => mocks.OrgUser()), + allUsers: () => new MockList(5, () => mocks.User()), + allPlatformUsers: () => new MockList(3, () => mocks.User()), + + // Projects projectByName: () => mocks.Project(), - groupByName: () => mocks.Group(), projectByGitUrl: () => mocks.Project(), - projectsByMetadata: () => mocks.Project(), + projectsByMetadata: () => [mocks.Project(), mocks.Project()], + allProjects: () => new MockList(5, () => mocks.Project()), + projectsByFactSearch: () => ({ count: 3, projects: [mocks.Project(), mocks.Project(), mocks.Project()] }), + environmentsByFactSearch: () => ({ count: 2, environments: [mocks.Environment(), mocks.Environment()] }), + + // Groups + groupByName: () => mocks.Group(), + groupByNameAndOrganization: () => mocks.Group(), + allGroups: () => new MockList(9), + allProjectsInGroup: () => new MockList(5), + + // Environments environmentByName: () => mocks.Environment(), environmentByOpenshiftProjectName: () => mocks.Environment(), - environmentWithBackup: () => () => mocks.Environment(null, { backups: [ - mocks.Backup(null, { environment: {} }), - ]}), - environmentWithBackups: () => mocks.Environment(null, { project: mocks.Project()}), - environmentWithFacts: () => mocks.Environment(), - environmentWithTask: ({projectName, envName}) => mocks.Environment(null, { - name: envName, - project: { - name: projectName, - problemsUi: true, - factsUi: true - }, - deployments: {}, problems: {}, facts: {}, environment: {}, - tasks: [ mocks.Task(null, { id: 1, environment: {} }) ] - }), - environmentWithTasks: () => mocks.Environment(null, { - deployments: {}, problems: {}, facts: {}, environment: {} - }), - environmentWithDeployment: ({projectName, envName}) => mocks.Environment(null, { - name: envName, - project: { - name: projectName, - problemsUi: true, - factsUi: true - }, - tasks: {}, problems: {}, facts: {}, environment: {}, - deployments: [ mocks.Deployment() ] - }), - environmentWithDeployments: () => mocks.Environment(null, { deployments: [ - mocks.Deployment(null, { environment: { openshiftProjectName: "lagoon-demo-main" }}), - mocks.Deployment(null, { environment: { openshiftProjectName: "lagoon-demo-main" }}) - ]}), - deploymentByRemoteId: () => mocks.Deployment(), - taskByRemoteId: () => mocks.Task(), - allProjectsFromFacts: () => [{ - count: 4, - projects: [ - mocks.Project(null, { environments: []}), - mocks.Project(null, { environments: [ - mocks.Environment(null, { name: 'master', facts: [ - mocks.Fact(null, { name: "laravel", value: "7.0.12", source: "laravel-version", category: "Framework", keyFact: true} ), - mocks.Fact(null, { name: "php-version", value: "7.1", source: "php-version", category: "Programming language", keyFact: true} ), - mocks.Fact(null, { name: "site-code-status", value: '200', source: "curl", category: "Performance", keyFact: true} ) - ]}) - ]}), - mocks.Project(null, { environments: [ - mocks.Environment(null, { name: 'master', facts: [ - mocks.Fact(null, { name: "express", value: "4.16.4", source: "npm", category: "Framework", keyFact: true} ), - mocks.Fact(null, { name: "node-js", value: "16.0.1", source: "node-version", category: "Programming language", keyFact: true} ), - mocks.Fact(null, { name: "site-code-status", value: '503', source: "curl", category: "Performance", keyFact: true} ) - ]}) - ]}), - mocks.Project() - ] - }], - allProjects: () => [ - mocks.Project(), - mocks.Project(), - mocks.Project(), - mocks.Project(), - mocks.Project() - ], - allOpenshifts: () => new MockList(9), - allProblems: () => new MockList(20), - allEnvironments: (parent, args = {}, context, info) => { + environmentById: () => mocks.Environment(), + environmentByKubernetesNamespaceName: () => mocks.Environment(), + allEnvironments: (parent, args = {}, _context, _info) => { const project = args.hasOwnProperty('project') ? args.project - : mocks.Project(null, {environments: []}); + : mocks.Project(null, { environments: [] }); return [ - mocks.Environment(null, {project, name: 'master'}), - mocks.Environment(null, {project, name: 'staging'}), - mocks.Environment(null, {project, name: 'development'}), - mocks.Environment(null, {project, name: 'pr-' + faker.random.number({min: 1, max: 50})}), - mocks.Environment(null, {project, name: 'pr-' + faker.random.number({min: 51, max: 100})}), - mocks.Environment(null, {project, name: 'pr-' + faker.random.number({min: 101, max: 150})}), + mocks.Environment(null, { project, name: 'master' }), + mocks.Environment(null, { project, name: 'staging' }), + mocks.Environment(null, { project, name: 'development' }), + mocks.Environment(null, { project, name: `pr-${faker.number.int({ min: 1, max: 50 })}` }), + mocks.Environment(null, { project, name: `pr-${faker.number.int({ min: 51, max: 100 })}` }), + mocks.Environment(null, { project, name: `pr-${faker.number.int({ min: 101, max: 150 })}` }), ]; }, - allGroups: () => new MockList(9), - allProjectsInGroup: () => new MockList(5), + environmentsByKubernetes: () => new MockList(5, () => mocks.Environment()), + + // Deploy targets + allOpenshifts: () => new MockList(9), + allKubernetes: () => new MockList(3, () => mocks.Kubernetes()), + + // Notifications + allNotifications: () => new MockList(5, () => mocks.Notification()), + + // Deployments + deploymentByRemoteId: () => mocks.Deployment(), + deploymentByName: () => mocks.Deployment(), + deploymentsByBulkId: () => new MockList(3, () => mocks.Deployment()), + deploymentsByFilter: () => new MockList(10, () => mocks.Deployment()), + + // Tasks + taskByRemoteId: () => mocks.Task(), + taskById: () => mocks.Task(), + taskByTaskName: () => mocks.Task(), + + // Problems + allProblems: () => new MockList(20), + problemSources: () => ['Harbor', 'Drutiny', 'Trivy', 'OWASP ZAP'], + + // Organizations + allOrganizations: () => new MockList(3, () => mocks.Organization()), + organizationById: () => mocks.Organization(), + organizationByName: () => mocks.Organization(), + orgProjectByName: () => mocks.OrgProject(), + checkBulkImportProjectsAndGroupsToOrganization: () => null, + + // Deploy target configs + allDeployTargetConfigs: () => [mocks.DeployTargetConfig()], + deployTargetConfigById: () => mocks.DeployTargetConfig(), + deployTargetConfigsByDeployTarget: () => [mocks.DeployTargetConfig()], + deployTargetConfigsByProjectId: () => [mocks.DeployTargetConfig()], + + // Advanced tasks + allAdvancedTaskDefinitions: () => [mocks.AdvancedTaskDefinitionCommand(), mocks.AdvancedTaskDefinitionImage()], + advancedTaskDefinitionById: () => mocks.AdvancedTaskDefinitionCommand(), + advancedTaskDefinitionArgumentById: () => mocks.AdvancedTaskDefinitionArgument(), + advancedTasksForEnvironment: () => new MockList(3), + + // Backups / Files + getBackupDownloadLinkByBackupId: () => faker.string.uuid(), + getDownloadLinkByTaskFileId: () => faker.string.uuid(), + getTaskFileUploadForm: () => null, + + // Env variables + getEnvVariablesByProjectEnvironmentName: () => [mocks.EnvKeyValue()], + + // Deprecated org helpers + getGroupProjectOrganizationAssociation: () => 'success', + getProjectGroupOrganizationAssociation: () => 'success', + + // Audit logs + getAuditLogs: () => new MockList(10, () => mocks.AuditLog()), + + // Retention policies + listAllRetentionPolicies: () => [mocks.HarborRetentionPolicy(), mocks.HistoryRetentionPolicy()], + + // Misc + lagoonVersion: () => '24.x.x', }); // // Mutation 'type' mock from typeDefs. -// @TODO These mutations have not been tested and may need refactoring. // mocks.Mutation = () => ({ + // Environment addOrUpdateEnvironment: () => mocks.Environment(), updateEnvironment: () => mocks.Environment(), - deleteEnvironment: () => faker.random.arrayElement(['success', `Error: unknown deploy type ${mocks.DeployType()}`]), + updateEnvironmentDeployTarget: () => mocks.Environment(), + deleteEnvironment: () => faker.helpers.arrayElement(['success', `Error: unknown deploy type ${mocks.DeployType()}`]), addOrUpdateEnvironmentStorage: () => mocks.EnvironmentStorage(), + idleOrUnidleEnvironment: () => mocks.Environment(), + + // Notifications addNotificationSlack: () => mocks.NotificationSlack(), updateNotificationSlack: () => mocks.NotificationSlack(), - deleteNotificationSlack: () => faker.random.arrayElement(['success', "Can't delete notification linked to projects"]), + deleteNotificationSlack: () => faker.helpers.arrayElement(['success', "Can't delete notification linked to projects"]), addNotificationRocketChat: () => mocks.NotificationRocketChat(), updateNotificationRocketChat: () => mocks.NotificationRocketChat(), - deleteNotificationRocketChat: () => faker.random.arrayElement(['success', "Can't delete notification linked to projects"]), + deleteNotificationRocketChat: () => faker.helpers.arrayElement(['success', "Can't delete notification linked to projects"]), addNotificationMicrosoftTeams: () => mocks.NotificationMicrosoftTeams(), updateNotificationMicrosoftTeams: () => mocks.NotificationMicrosoftTeams(), - deleteNotificationMicrosoftTeams: () => faker.random.arrayElement(['success', "Can't delete notification linked to projects"]), + deleteNotificationMicrosoftTeams: () => faker.helpers.arrayElement(['success', "Can't delete notification linked to projects"]), addNotificationEmail: () => mocks.NotificationEmail(), updateNotificationEmail: () => mocks.NotificationEmail(), - deleteNotificationEmail: () => faker.random.arrayElement(['success', "Can't delete notification linked to projects"]), + deleteNotificationEmail: () => faker.helpers.arrayElement(['success', "Can't delete notification linked to projects"]), + addNotificationWebhook: () => mocks.NotificationWebhook(), + updateNotificationWebhook: () => mocks.NotificationWebhook(), + deleteNotificationWebhook: () => faker.helpers.arrayElement(['success', "Can't delete notification linked to projects"]), addNotificationToProject: () => mocks.Project(), removeNotificationFromProject: () => mocks.Project(), + + // Deploy targets addOpenshift: () => mocks.Openshift(), updateOpenshift: () => mocks.Openshift(), deleteOpenshift: () => 'success', + addKubernetes: () => mocks.Kubernetes(), + updateKubernetes: () => mocks.Kubernetes(), + deleteKubernetes: () => 'success', + + // Projects addProject: () => mocks.Project(), updateProject: () => mocks.Project(), deleteProject: () => 'success', + deleteAllProjects: () => 'success', + updateProjectMetadata: () => mocks.Project(), + removeProjectMetadataByKey: () => mocks.Project(), + + // SSH keys addSshKey: () => mocks.SshKey(), updateSshKey: () => mocks.SshKey(), deleteSshKey: () => 'success', deleteSshKeyById: () => 'success', + addUserSSHPublicKey: () => mocks.SshKey(), + updateUserSSHPublicKey: () => mocks.SshKey(), + deleteUserSSHPublicKey: () => 'success', + + // Users addUser: () => mocks.User(), updateUser: () => mocks.User(), deleteUser: () => 'success', + resetUserPassword: () => 'success', + addPlatformRoleToUser: () => mocks.User(), + removePlatformRoleFromUser: () => mocks.User(), + + // Deployments addDeployment: () => mocks.Deployment(), deleteDeployment: () => 'success', updateDeployment: () => mocks.Deployment(), - cancelDeployment: () => faker.random.arrayElement(['success', 'Deployment not cancelled, reason: Too slow.']), + cancelDeployment: () => faker.helpers.arrayElement(['success', 'Deployment not cancelled, reason: Too slow.']), + deployEnvironmentLatest: () => 'success', + deployEnvironmentBranch: () => 'success', + deployEnvironmentPullrequest: () => 'success', + deployEnvironmentPromote: () => 'success', + bulkDeployEnvironmentLatest: () => faker.string.uuid(), + switchActiveStandby: () => 'success', + + // Backups addBackup: () => mocks.Backup(), deleteBackup: () => 'success', + deleteRestore: () => mocks.Backup(), addRestore: () => mocks.Restore(), updateRestore: () => mocks.Restore(), + + // Env variables addEnvVariable: () => mocks.EnvKeyValue(), + addOrUpdateEnvVariableByName: () => mocks.EnvKeyValue(), + addOrUpdateStorageOnEnvironment: () => mocks.EnvironmentStorage(), deleteEnvVariable: () => 'success', + deleteEnvVariableByName: () => 'success', + + // Tasks addTask: () => mocks.Task(), taskDrushArchiveDump: () => mocks.Task(), taskDrushSqlDump: () => mocks.Task(), @@ -687,17 +946,21 @@ mocks.Mutation = () => ({ taskDrushCron: () => mocks.Task(), taskDrushSqlSync: () => mocks.Task(), taskDrushRsyncFiles: () => mocks.Task(), + taskDrushUserLogin: () => mocks.Task(), deleteTask: () => 'success', updateTask: () => mocks.Task(), - cancelTask: () => faker.random.arrayElement(['success', 'Task not cancelled, reason: Too slow.']), - setEnvironmentServices: () => [ mocks.EnvironmentService() ], + cancelTask: () => faker.helpers.arrayElement(['success', 'Task not cancelled, reason: Too slow.']), + invokeRegisteredTask: () => mocks.Task(), uploadFilesForTask: () => mocks.Task(), deleteFilesForTask: () => 'success', - deployEnvironmentLatest: () => 'success', - deployEnvironmentBranch: () => 'success', - deployEnvironmentPullrequest: () => 'success', - deployEnvironmentPromote: () => 'success', - switchActiveStandby: () => 'success', + + // Services + setEnvironmentServices: () => [mocks.EnvironmentService()], + addOrUpdateEnvironmentService: () => mocks.EnvironmentService(), + deleteEnvironmentService: () => 'success', + stopOrStartEnvironmentService: () => 'success', + + // Groups addGroup: () => mocks.Group(), updateGroup: () => mocks.Group(), deleteGroup: () => 'success', @@ -705,6 +968,78 @@ mocks.Mutation = () => ({ removeUserFromGroup: () => mocks.Group(), addGroupsToProject: () => mocks.Project(), removeGroupsFromProject: () => mocks.Project(), + + // Problems + addProblem: () => mocks.Problem(), + deleteProblem: () => 'success', + deleteProblemsFromSource: () => 'success', + + // Facts + addFact: () => mocks.Fact(), + addFacts: () => [mocks.Fact(), mocks.Fact()], + addFactsByName: () => [mocks.Fact()], + deleteFact: () => 'success', + deleteFactsFromSource: () => 'success', + addFactReference: () => mocks.Fact(), + deleteFactReference: () => 'success', + deleteAllFactReferencesByFactId: () => 'success', + + // Organizations + addOrganization: () => mocks.Organization(), + updateOrganization: () => mocks.Organization(), + deleteOrganization: () => 'success', + addAdminToOrganization: () => mocks.Organization(), + removeAdminFromOrganization: () => mocks.Organization(), + addGroupToOrganization: () => mocks.Organization(), + addExistingGroupToOrganization: () => mocks.Organization(), + addExistingProjectToOrganization: () => mocks.Project(), + bulkImportProjectsAndGroupsToOrganization: () => null, + removeProjectFromOrganization: () => 'success', + addUserToOrganization: () => mocks.Organization(), + removeUserFromOrganization: () => 'success', + removeUserFromOrganizationGroups: () => 'success', + addDeployTargetToOrganization: () => mocks.Organization(), + removeDeployTargetFromOrganization: () => 'success', + + // Deploy target configs + addDeployTargetConfig: () => mocks.DeployTargetConfig(), + updateDeployTargetConfig: () => mocks.DeployTargetConfig(), + deleteDeployTargetConfig: () => 'success', + + // Routes + addRouteToProject: () => mocks.Route(), + updateRouteOnProject: () => mocks.Route(), + addRouteAlternativeDomains: () => mocks.Route(), + removeRouteAlternativeDomain: () => mocks.Route(), + addRouteAnnotation: () => mocks.Route(), + removeRouteAnnotation: () => mocks.Route(), + addPathRoutesToRoute: () => mocks.Route(), + removePathRouteFromRoute: () => mocks.Route(), + addOrUpdateRouteOnEnvironment: () => mocks.Route(), + activeStandbyRouteMove: () => mocks.Route(), + removeRouteFromEnvironment: () => mocks.Route(), + deleteRoute: () => 'success', + updateAutogeneratedRouteConfigOnProject: () => null, + updateAutogeneratedRouteConfigOnEnvironment: () => null, + removeAutogeneratedRouteConfigFromProject: () => 'success', + removeAutogeneratedRouteConfigFromEnvironment: () => 'success', + + // Advanced tasks + addAdvancedTaskDefinition: () => mocks.AdvancedTaskDefinitionCommand(), + updateAdvancedTaskDefinition: () => mocks.AdvancedTaskDefinitionCommand(), + deleteAdvancedTaskDefinition: () => 'success', + + // Retention policies + createHarborRetentionPolicy: () => mocks.HarborRetentionPolicy(), + updateHarborRetentionPolicy: () => mocks.HarborRetentionPolicy(), + deleteHarborRetentionPolicy: () => 'success', + addHarborRetentionPolicyLink: () => 'success', + removeHarborRetentionPolicyLink: () => 'success', + createHistoryRetentionPolicy: () => mocks.HistoryRetentionPolicy(), + updateHistoryRetentionPolicy: () => mocks.HistoryRetentionPolicy(), + deleteHistoryRetentionPolicy: () => 'success', + addHistoryRetentionPolicyLink: () => 'success', + removeHistoryRetentionPolicyLink: () => 'success', }); // diff --git a/services/api/src/models/environment.ts b/services/api/src/models/environment.ts index 02b70af153..97274b8cee 100644 --- a/services/api/src/models/environment.ts +++ b/services/api/src/models/environment.ts @@ -2,12 +2,12 @@ import moment from 'moment'; import { Pool } from 'mariadb'; import { query, knex } from '../util/db'; import { logger } from '../loggers/logger'; -import { DeployType } from '@lagoon/commons/dist/types'; +import { DeployType } from '../commons/types'; export interface Environment { id?: number; // int(11) NOT NULL AUTO_INCREMENT, name?: string; // varchar(100) COLLATE utf8_bin DEFAULT NULL, - project?: Number; // int(11) DEFAULT NULL, + project?: number; // int(11) DEFAULT NULL, deployType?: DeployType; environmentType?: string; // enum('production','development') COLLATE utf8_bin NOT NULL, openshiftProjectName?: string; // varchar(100) COLLATE utf8_bin DEFAULT NULL, @@ -16,7 +16,7 @@ export interface Environment { deleted?: string; route?: string; // varchar(300) COLLATE utf8_bin DEFAULT NULL, routes?: string; // text COLLATE utf8_bin DEFAULT NULL, - autoIdle?: Boolean; // int(1) NOT NULL DEFAULT 1, + autoIdle?: boolean; // int(1) NOT NULL DEFAULT 1, deployBaseRef?: string; // varchar(100) COLLATE utf8_bin DEFAULT NULL, deployHeadRef?: string; // varchar(100) COLLATE utf8_bin DEFAULT NULL, deployTitle?: string; // varchar(300) COLLATE utf8_bin DEFAULT NULL, @@ -71,25 +71,24 @@ export const Environment = (clients: { const environmentsByProjectId = projectEnvironments; // Needed for local Dev - Required if not connected to openshift - const errorCatcherFn = (msg, responseObj) => (err) => { - const errMsg = - err && err.status && err.message - ? `${err.status} : ${err.message} : ${err.headers} : ${err.url}` - : `err undefined`; + const _errorCatcherFn = (msg, responseObj) => (err) => { + const errMsg = err && err.status && err.message + ? `${err.status} : ${err.message} : ${err.headers} : ${err.url}` + : 'err undefined'; logger.error(`${msg}: ${errMsg}`); return { ...responseObj }; }; const environmentStorageMonthByEnvironmentId = async (eid, month) => { - let q = knex('environment_storage') + const q = knex('environment_storage') .select(knex.raw('SUM(kib_used) as kib_used')) - .select(knex.raw(`max(DATE_FORMAT(updated, '%Y-%m')) as month`)) + .select(knex.raw('max(DATE_FORMAT(updated, \'%Y-%m\')) as month')) .where('environment', eid) .andWhere( - knex.raw(`YEAR(updated) = YEAR(STR_TO_DATE(?, '%Y-%m'))`, month), + knex.raw('YEAR(updated) = YEAR(STR_TO_DATE(?, \'%Y-%m\'))', month), ) .andWhere( - knex.raw(`MONTH(updated) = MONTH(STR_TO_DATE(?, '%Y-%m'))`, month), + knex.raw('MONTH(updated) = MONTH(STR_TO_DATE(?, \'%Y-%m\'))', month), ); const rows = await query(sqlClientPool, q.toString()); @@ -136,10 +135,9 @@ export const Environment = (clients: { } // calculate the month in format `YYYY-MM`. getMonth() does not return with a leading zero and starts its index at 0 as well. - const month_leading_zero = - interested_month_start.getMonth() + 1 < 10 - ? `0${interested_month_start.getMonth() + 1}` - : interested_month_start.getMonth() + 1; + const month_leading_zero = interested_month_start.getMonth() + 1 < 10 + ? `0${interested_month_start.getMonth() + 1}` + : interested_month_start.getMonth() + 1; const month = `${interested_month_start.getFullYear()}-${month_leading_zero}`; // Created Date is created after the interested month: Ran for 0 hours in the requested month @@ -149,9 +147,9 @@ export const Environment = (clients: { // Environment was deleted before the month we are interested in: Ran for 0 hours in the requested month if ( - deleted_date < interested_month_start && - moment(deleted_date).format('YYYY-MM-DD HH:mm:ss') !== - '0000-00-00 00:00:00' + deleted_date < interested_month_start + && moment(deleted_date).format('YYYY-MM-DD HH:mm:ss') + !== '0000-00-00 00:00:00' ) { return { month, hours: 0 }; } @@ -168,8 +166,8 @@ export const Environment = (clients: { } if ( - deleted === '0000-00-00 00:00:00' || - deleted_date > interested_month_end + deleted === '0000-00-00 00:00:00' + || deleted_date > interested_month_end ) { // Environment is not deleted yet or was deleted after the interested month date_to = interested_month_end; @@ -375,7 +373,7 @@ export const Environment = (clients: { } catch (e) { logger.error( `Elastic Search Query Error: ${ - JSON.stringify(e) != '{}' ? JSON.stringify(e) : e + JSON.stringify(e) !== '{}' ? JSON.stringify(e) : e }`, ); // const noHits = { total: 0 }; @@ -397,59 +395,53 @@ export const Environment = (clients: { const calculateHitsFromESData = (legacyResult, newResult) => { // 0 hits found in elasticsearch, don't even try to generate monthly counts if ( - legacyResult.hits.total.value === 0 && - newResult.hits.total.value === 0 + legacyResult.hits.total.value === 0 + && newResult.hits.total.value === 0 ) { return { total: 0 }; } - var total = 0; - - const legacyBuckets = - legacyResult && - legacyResult.aggregations && - legacyResult.aggregations.hourly && - legacyResult.aggregations.hourly.buckets - ? legacyResult.aggregations.hourly.buckets - : 0; - const legacyResultCount = - legacyResult && - legacyResult.aggregations && - legacyResult.aggregations.hourly && - legacyResult.aggregations.hourly.buckets && - legacyResult.aggregations.hourly.buckets.length - ? legacyResult.aggregations.hourly.buckets.length - : 0; - const legacyAvg = - legacyResult && - legacyResult.aggregations && - legacyResult.aggregations.average && - legacyResult.aggregations.average.value - ? parseInt(legacyResult.aggregations.average.value) - : 0; - - const newBuckets = - newResult && - newResult.aggregations && - newResult.aggregations.hourly && - newResult.aggregations.hourly.buckets - ? newResult.aggregations.hourly.buckets - : 0; - const newResultCount = - newResult && - newResult.aggregations && - newResult.aggregations.hourly && - newResult.aggregations.hourly.buckets && - newResult.aggregations.hourly.buckets.length - ? newResult.aggregations.hourly.buckets.length - : 0; - const newAvg = - newResult && - newResult.aggregations && - newResult.aggregations.average && - newResult.aggregations.average.value - ? parseInt(newResult.aggregations.average.value) - : 0; + let total = 0; + + const legacyBuckets = legacyResult + && legacyResult.aggregations + && legacyResult.aggregations.hourly + && legacyResult.aggregations.hourly.buckets + ? legacyResult.aggregations.hourly.buckets + : 0; + const legacyResultCount = legacyResult + && legacyResult.aggregations + && legacyResult.aggregations.hourly + && legacyResult.aggregations.hourly.buckets + && legacyResult.aggregations.hourly.buckets.length + ? legacyResult.aggregations.hourly.buckets.length + : 0; + const legacyAvg = legacyResult + && legacyResult.aggregations + && legacyResult.aggregations.average + && legacyResult.aggregations.average.value + ? parseInt(legacyResult.aggregations.average.value) + : 0; + + const newBuckets = newResult + && newResult.aggregations + && newResult.aggregations.hourly + && newResult.aggregations.hourly.buckets + ? newResult.aggregations.hourly.buckets + : 0; + const newResultCount = newResult + && newResult.aggregations + && newResult.aggregations.hourly + && newResult.aggregations.hourly.buckets + && newResult.aggregations.hourly.buckets.length + ? newResult.aggregations.hourly.buckets.length + : 0; + const newAvg = newResult + && newResult.aggregations + && newResult.aggregations.average + && newResult.aggregations.average.value + ? parseInt(newResult.aggregations.average.value) + : 0; /* foreach hourlybucket (#both result buckets should have the exact same amount of buckets) @@ -465,18 +457,18 @@ export const Environment = (clients: { const count = newResultCount > 0 ? newResultCount : legacyResultCount; for (let i = 0; i < count; i++) { if ( - newResultCount !== 0 && - newBuckets[i] && - newBuckets[i].count && - newBuckets[i].count.value !== 0 + newResultCount !== 0 + && newBuckets[i] + && newBuckets[i].count + && newBuckets[i].count.value !== 0 ) { // We have new logging data, use this for total total += newBuckets[i].count.value; } else if ( - legacyResultCount !== 0 && - legacyBuckets[i] && - legacyBuckets[i].count && - legacyBuckets[i].count.value !== 0 + legacyResultCount !== 0 + && legacyBuckets[i] + && legacyBuckets[i].count + && legacyBuckets[i].count.value !== 0 ) { // We have legacy data total += legacyBuckets[i].count.value; diff --git a/services/api/src/models/group.ts b/services/api/src/models/group.ts index 9304dc1c39..d64ebc138c 100644 --- a/services/api/src/models/group.ts +++ b/services/api/src/models/group.ts @@ -1,15 +1,15 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; +import type { GroupRepresentation, KeycloakAdminClient } from '@s3pweb/keycloak-admin-client-cjs'; import { asyncPipe, decodeJSONBase64, encodeJSONBase64, -} from '@lagoon/commons/dist/util/func'; +} from '../commons/util/func'; import pickNonNil from '../util/pickNonNil'; import { toNumber, getErrorMessage } from '../util/func'; import { getConfigFromEnv } from '../util/config'; import { logger } from '../loggers/logger'; -import type { GroupRepresentation, KeycloakAdminClient } from '@s3pweb/keycloak-admin-client-cjs'; import { isNetworkError } from '../clients/keycloak-admin'; import { User } from './user'; import { @@ -108,8 +108,8 @@ export class GroupNotFoundError extends Error { const internalGroupTypes = [GroupType.ROLE_SUBGROUP]; export const isRoleSubgroup = R.pathEq( + 'role-subgroup', ['attributes', 'type', 0], - 'role-subgroup' ); const isInternalGroup = (group: SparseGroup) => { @@ -122,10 +122,9 @@ const isInternalGroup = (group: SparseGroup) => { } return false; -} +}; -const attributeKVOrNull = (key: string, group: GroupRepresentation) => - String(R.pathOr(null, ['attributes', key], group)); +const attributeKVOrNull = (key: string, group: GroupRepresentation) => String(R.pathOr(null, ['attributes', key], group)); const parseGroupType = (type: string): GroupType | null => { if (Object.values(GroupType).includes(type as GroupType)) { @@ -155,16 +154,16 @@ export interface GroupModel { deleteGroup: (id: string) => Promise removeGroupFromOrganization: (id: string) => Promise addUserToGroup: (user: User, groupInput: GroupInput, roleName: string) => Promise - removeUserFromGroup: (user: User, group: Group ) => Promise - removeUserFromGroups: (user: User,groups: Group[]) => Promise + removeUserFromGroup: (user: User, group: Group) => Promise + removeUserFromGroups: (user: User, groups: Group[]) => Promise addProjectToGroup: (projectId: number, groupInput: any) => Promise removeProjectFromGroup: (projectId: number, group: Group) => Promise removeProjectFromGroups: (projectId: number, groups: Group[]) => Promise transformKeycloakGroups: (keycloakGroups: GroupRepresentation[]) => Promise getGroupMembership: (group: Group) => Promise getGroupMemberCount: (group: Group) => Promise - removeNonProjectDefaultUsersFromGroup: (group: Group, project: String) => Promise - purgeGroupCache: (group: Pick, membersOnly: Boolean) => Promise + removeNonProjectDefaultUsersFromGroup: (group: Group, project: string) => Promise + purgeGroupCache: (group: Pick, membersOnly: boolean) => Promise } export const Group = (clients: { @@ -174,7 +173,7 @@ export const Group = (clients: { const { keycloakAdminClient } = clients; const transformKeycloakGroups = async ( - keycloakGroups: GroupRepresentation[] + keycloakGroups: GroupRepresentation[], ): Promise => { // Map from keycloak object to group object const groups = await Promise.all(keycloakGroups.map( @@ -186,10 +185,10 @@ export const Group = (clients: { attributes: keycloakGroup.attributes, subGroups: keycloakGroup.subGroups, organization: await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(keycloakGroup.id), // if it exists set it or null - }) + }), )); - let groupsWithGroupsAndMembers = []; + const groupsWithGroupsAndMembers = []; for (const group of groups) { const subGroups = R.reject(isRoleSubgroup)(group.subGroups); @@ -205,7 +204,7 @@ export const Group = (clients: { }; const createGroupFromKeycloak = (group: KeycloakLagoonGroup, projectsArray: number[], organizationAttr: number): SparseGroup => { - const groupAttr = group.attributes?.['type']?.[0]; + const groupAttr = group.attributes?.type?.[0]; if (!group.id) { throw new Error('Missing group id'); @@ -338,11 +337,10 @@ export const Group = (clients: { }): Promise => { if (groupInput.id) { return loadSparseGroupById(groupInput.id); - } else if (groupInput.name) { + } if (groupInput.name) { return loadSparseGroupByName(groupInput.name); - } else { - throw new Error('You must provide a group id or name'); } + throw new Error('You must provide a group id or name'); }; /** @@ -354,26 +352,25 @@ export const Group = (clients: { ): Promise => { if (groupInput.id) { return loadGroupById(groupInput.id); - } else if (groupInput.name) { + } if (groupInput.name) { return loadGroupByName(groupInput.name); - } else { - throw new Error('You must provide a group id or name'); } + throw new Error('You must provide a group id or name'); }; // load all keycloak groups using pagination const loadAllKeycloakGroups = async (): Promise => { let groups = []; let first = 0; - let pageSize = 200; + const pageSize = 200; let fetchedGroups; while (true) { fetchedGroups = await keycloakAdminClient.groups.find({ briefRepresentation: false, - first: first, - max: pageSize + first, + max: pageSize, }); - groups = groups.concat(fetchedGroups) + groups = groups.concat(fetchedGroups); first += pageSize; if (fetchedGroups.length < pageSize) { break; @@ -385,7 +382,7 @@ export const Group = (clients: { const loadAllGroups = async (): Promise => { const keycloakGroups = await loadAllKeycloakGroups(); - let fullGroups: KeycloakLagoonGroup[] = []; + const fullGroups: KeycloakLagoonGroup[] = []; for (const group of keycloakGroups) { fullGroups.push(await loadKeycloakChildren(group)); } @@ -398,7 +395,7 @@ export const Group = (clients: { depth: number | null = null, ): Promise => { // Subgroups already loaded. - if ("groups" in group && "allGroups" in group) { + if ('groups' in group && 'allGroups' in group) { return group; } @@ -411,13 +408,13 @@ export const Group = (clients: { }; } - let keycloakGroups = await keycloakAdminClient.groups.listSubGroups({ + const keycloakGroups = await keycloakAdminClient.groups.listSubGroups({ parentId: group.id, max: group.subGroupCount, briefRepresentation: false, // Returns attributes }); - let subGroups: HieararchicalGroup[] = []; + const subGroups: HieararchicalGroup[] = []; for (const keycloakGroup of keycloakGroups) { // get the projectids from the group_project database table instead of group attributes const projectIds = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(keycloakGroup.id); @@ -425,12 +422,11 @@ export const Group = (clients: { const subGroup = createGroupFromKeycloak(keycloakGroup, projectIds, organizationId); - const groupWithSubgroups = await loadSubGroups(subGroup, depth ? depth -1 : null); + const groupWithSubgroups = await loadSubGroups(subGroup, depth ? depth - 1 : null); subGroups.push({ ...subGroup, allGroups: groupWithSubgroups.allGroups, - groups: groupWithSubgroups.allGroups.filter((group: HieararchicalGroup): boolean => - !isInternalGroup(group) + groups: groupWithSubgroups.allGroups.filter((group: HieararchicalGroup): boolean => !isInternalGroup(group), ), }); } @@ -438,21 +434,20 @@ export const Group = (clients: { const groupWithSubgroups = { ...group, allGroups: subGroups, - groups: subGroups.filter((group: HieararchicalGroup): boolean => - !isInternalGroup(group) + groups: subGroups.filter((group: HieararchicalGroup): boolean => !isInternalGroup(group), ), }; - return groupWithSubgroups + return groupWithSubgroups; }; - const loadKeycloakChildren = async(group: KeycloakLagoonGroup): Promise => { - let keycloakGroups = await keycloakAdminClient.groups.listSubGroups({ + const loadKeycloakChildren = async (group: KeycloakLagoonGroup): Promise => { + const keycloakGroups = await keycloakAdminClient.groups.listSubGroups({ parentId: group.id, briefRepresentation: false, // Returns attributes }); - let subGroups: KeycloakLagoonGroup[] = []; + const subGroups: KeycloakLagoonGroup[] = []; for (const keycloakGroup of keycloakGroups) { const groupWithSubgroups = await loadKeycloakChildren(keycloakGroup); subGroups.push({ @@ -465,25 +460,24 @@ export const Group = (clients: { ...group, subGroups, }; - } + }; - const loadParentGroup = async (groupInput: Group): Promise => - asyncPipe( - R.prop('path'), - R.split('/'), - R.nth(-2), - R.cond([[R.isEmpty, R.always(null)], [R.T, loadGroupByName]]) - )(groupInput); + const loadParentGroup = async (groupInput: Group): Promise => asyncPipe( + R.prop('path'), + R.split('/'), + R.nth(-2), + R.cond([[R.isEmpty, R.always(null)], [R.T, loadGroupByName]]), + )(groupInput); // Recursive function to load membership "up" the group chain - const getMembersFromGroupAndParents = async ( - group: Group + const _getMembersFromGroupAndParents = async ( + group: Group, ): Promise => { const members = R.prop('members', group); const parentGroup = await loadParentGroup(group); const parentMembers = parentGroup - ? await getMembersFromGroupAndParents(parentGroup) + ? await _getMembersFromGroupAndParents(parentGroup) : []; return [...members, ...parentMembers]; @@ -491,7 +485,7 @@ export const Group = (clients: { // Recursive function to load projects "up" the group chain const getProjectsFromGroupAndParents = async ( - group: Group + group: Group, ): Promise => { // get the projectids from the group_project database table instead of group attributes const projectIds = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(group.id); @@ -504,13 +498,13 @@ export const Group = (clients: { return [ // @ts-ignore ...projectIds, - ...parentProjectIds + ...parentProjectIds, ]; }; // Recursive function to load projects "down" the group chain const getProjectsFromGroupAndSubgroups = async ( - group: Group + group: Group, ): Promise => { try { // get the projectids from the group_project database table instead of group attributes @@ -528,14 +522,14 @@ export const Group = (clients: { const projectIdsArray = [ // @ts-ignore ...groupProjectIds, - ...subGroupProjectIds + ...subGroupProjectIds, ]; // remove deleted projects from the result to prevent null errors in user queries const existingProjects = await projectHelpers(sqlClientPool).getAllProjectsIn(projectIdsArray); - let existingProjectsIds = []; + const existingProjectsIds = []; existingProjectsIds.push(...existingProjects.map(epi => epi.id)); - return projectIdsArray.filter(item => existingProjectsIds.some(existingProjectsIds => existingProjectsIds === item)) - } catch (err) { + return projectIdsArray.filter(item => existingProjectsIds.some(existingProjectsIds => existingProjectsIds === item)); + } catch (_err) { return []; } }; @@ -543,17 +537,17 @@ export const Group = (clients: { // return only project ids that still exist in lagoon in the response for which projects this group has assigned // in the past some groups could have been deleted from lagoon and their `attribute` in keycloak remained const getProjectsFromGroup = async ( - group: Group + group: Group, ): Promise => { try { // get the projectids from the group_project database table instead of group attributes const groupProjectIds = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(group.id); // remove deleted projects from the result to prevent null errors in user queries const existingProjects = await projectHelpers(sqlClientPool).getAllProjectsIn(groupProjectIds); - let existingProjectsIds = []; + const existingProjectsIds = []; existingProjectsIds.push(...existingProjects.map(epi => epi.id)); - return existingProjectsIds - } catch (err) { + return existingProjectsIds; + } catch (_err) { return []; } }; @@ -568,7 +562,7 @@ export const Group = (clients: { membership = await getGroupMembershipCache(group.id); } - if (membership.length == 0) { + if (membership.length === 0) { const UserModel = User(clients); const roleSubgroups = group.subGroups.filter(isRoleSubgroup); @@ -601,9 +595,9 @@ export const Group = (clients: { }; const getGroupMemberCount = async ( - group: Group + group: Group, ): Promise => { - const membership = await getGroupMembership(group) + const membership = await getGroupMembership(group); return membership.length; }; @@ -703,7 +697,7 @@ export const Group = (clients: { { id: groupInput.id, }, - //@ts-ignore + // @ts-ignore { ...pickNonNil(['name', 'attributes'], groupInput), }, @@ -724,12 +718,12 @@ export const Group = (clients: { } } - if (groupInput.name && oldGroup.name != groupInput.name) { + if (groupInput.name && oldGroup.name !== groupInput.name) { try { const newGroup = await loadSparseGroupById(groupInput.id); const fullGroup = await loadSubGroups(newGroup, 1); for (const subGroup of fullGroup.allGroups) { - if (subGroup.type == GroupType.ROLE_SUBGROUP) { + if (subGroup.type === GroupType.ROLE_SUBGROUP) { await keycloakAdminClient.groups.update( { id: subGroup.id, @@ -783,8 +777,8 @@ export const Group = (clients: { attributes: { ...group.attributes, // lagoon-organization attribute is removed for legacy reasons only, theses values are stored in the api-db now - "lagoon-organization": [""] - } + 'lagoon-organization': [''], + }, }); await groupHelpers(sqlClientPool).removeGroupFromOrganization(id); await purgeGroupCache(group); @@ -860,7 +854,7 @@ export const Group = (clients: { ): Promise => { const groupMembers = await getGroupMembership(group, false); const userMembership = groupMembers.find( - (membership: GroupMembership) => membership.user.id == user.id, + (membership: GroupMembership) => membership.user.id === user.id, ); if (userMembership) { @@ -995,12 +989,11 @@ export const Group = (clients: { const removeNonProjectDefaultUsersFromGroup = async ( group: Group, - project: String, + project: string, ): Promise => { const groupMembers = await getGroupMembership(group, false); const nonDefaultMembers = groupMembers.filter( - (member: GroupMembership) => - member.user.email != `default-user@${project}`, + (member: GroupMembership) => member.user.email !== `default-user@${project}`, ); for (const member of nonDefaultMembers) { @@ -1012,7 +1005,7 @@ export const Group = (clients: { const purgeGroupCache = async ( group: Pick, - membersOnly: Boolean = false, + membersOnly: boolean = false, ): Promise => { try { if (!membersOnly) { @@ -1038,7 +1031,7 @@ export const Group = (clients: { const nameCacheKey = `cache:keycloak:group-id:${group.name}`; try { - const data = encodeJSONBase64(group) + const data = encodeJSONBase64(group); await redisClient .multi() .set(nameCacheKey, group.id) @@ -1064,7 +1057,7 @@ export const Group = (clients: { const getGroupCacheById = async (groupId: string): Promise => { try { - let data = await get(`cache:keycloak:group:${groupId}`); + const data = await get(`cache:keycloak:group:${groupId}`); if (!data) { return null; @@ -1100,7 +1093,7 @@ export const Group = (clients: { groupId: string, ): Promise => { try { - let data = await get(`cache:keycloak:group-members:${groupId}`); + const data = await get(`cache:keycloak:group-members:${groupId}`); if (!data) { return []; @@ -1139,6 +1132,6 @@ export const Group = (clients: { getGroupMembership, getGroupMemberCount, removeNonProjectDefaultUsersFromGroup, - purgeGroupCache + purgeGroupCache, }; }; diff --git a/services/api/src/models/user.ts b/services/api/src/models/user.ts index c6bc5aaaf8..d94f7ffc8d 100644 --- a/services/api/src/models/user.ts +++ b/services/api/src/models/user.ts @@ -42,7 +42,6 @@ export interface User { isFederatedUser?: boolean; } - interface FederatedIdentity { hasIdentities?: boolean; gitlabId?: string; @@ -76,15 +75,15 @@ export interface UserModel { projectId: number, userGroups: Group[] ) => Promise; - addUser: (userInput: User, resetPassword?: Boolean) => Promise; + addUser: (userInput: User, resetPassword?: boolean) => Promise; addPlatformRoleToUser: (userInput: User, role: PlatformRole) => Promise; removePlatformRoleFromUser: (userInput: User, role: PlatformRole) => Promise; updateUser: (userInput: UserEdit) => Promise; deleteUser: (id: string) => Promise; resetUserPassword: (id: string) => Promise; - userLastAccessed: (userInput: User) => Promise; + userLastAccessed: (userInput: User) => Promise; transformKeycloakUsers: (keycloakUsers: UserRepresentation[]) => Promise; - getFullUserDetails: (userInput: User) => Promise; + getFullUserDetails: (userInput: User) => Promise; fetchUserTwoFactor: (user: User) => Promise; } @@ -124,28 +123,28 @@ const attrLagoonOrgOwnerLens = R.compose( // @ts-ignore attrLens, lagoonOrganizationsLens, - R.lensPath([0]) + R.lensPath([0]), ); const attrLagoonOrgAdminLens = R.compose( // @ts-ignore attrLens, lagoonOrganizationsAdminLens, - R.lensPath([0]) + R.lensPath([0]), ); const attrLagoonOrgViewerLens = R.compose( // @ts-ignore attrLens, lagoonOrganizationsViewerLens, - R.lensPath([0]) + R.lensPath([0]), ); const attrCommentLens = R.compose( // @ts-ignore attrLens, commentLens, - R.lensPath([0]) + R.lensPath([0]), ); export const User = (clients: { @@ -157,35 +156,32 @@ export const User = (clients: { // filter for user attributes like `lagoon-organizations` const filterUsersByAttribute = ( users: User[], - filterFn: AttributeFilterFn - ): User[] => - R.filter((user: User) => - R.pipe( - R.toPairs, - R.reduce((isMatch: boolean, attribute: [string, string[]]): boolean => { - if (!isMatch) { - return filterFn({ - name: attribute[0], - value: attribute[1] - }); - } - - return isMatch; - }, false) - )(user.attributes) - )(users); - + filterFn: AttributeFilterFn, + ): User[] => R.filter((user: User) => R.pipe( + R.toPairs, + R.reduce((isMatch: boolean, attribute: [string, string[]]): boolean => { + if (!isMatch) { + return filterFn({ + name: attribute[0], + value: attribute[1], + }); + } + + return isMatch; + }, false), + )(user.attributes), + )(users); const fetchUserTwoFactor = async (user: User): Promise => { const credTypes = await keycloakAdminClient.users.getCredentials({ - id: user.id + id: user.id, }); - let hasTwoFactor = false - for(const cred of credTypes) { - switch (cred.type.replace(/"/g, "")) { - case "webauthn": - case "otp": - hasTwoFactor = true + let hasTwoFactor = false; + for (const cred of credTypes) { + switch (cred.type.replace(/"/g, '')) { + case 'webauthn': + case 'otp': + hasTwoFactor = true; break; default: break; @@ -194,14 +190,13 @@ export const User = (clients: { return hasTwoFactor; }; - const fetchFederatedIdentities = async (user: User): Promise => { - let userFed: FederatedIdentity = { + const userFed: FederatedIdentity = { hasIdentities: false, gitlabId: null, }; const identities = await keycloakAdminClient.users.listFederatedIdentities({ - id: user.id + id: user.id, }); if (identities.length > 0) { @@ -209,16 +204,16 @@ export const User = (clients: { } const gitlabIdentity = R.find( - R.propEq('identityProvider', 'gitlab'), - identities + R.propEq('gitlab', 'identityProvider'), + identities, ); // @ts-ignore - userFed.gitlabId = R.defaultTo(undefined, R.prop('userId', gitlabIdentity)) + userFed.gitlabId = R.defaultTo(undefined, R.prop('userId', gitlabIdentity)); return userFed; }; const transformKeycloakUsers = async ( - keycloakUsers: UserRepresentation[] + keycloakUsers: UserRepresentation[], ): Promise => { // Map from keycloak object to user object const users = keycloakUsers.map( @@ -230,26 +225,26 @@ export const User = (clients: { R.set(commentLens, R.view(attrCommentLens, keycloakUser)), // set the user created time R.set(R.lensPath(['created']), new Date(keycloakUser.createdTimestamp).toISOString().slice(0, 19).replace('T', ' ') || null), - )(keycloakUser) + )(keycloakUser as any), ); - let usersWithGitlabIdFetch = []; + const usersWithGitlabIdFetch = []; for (const user of users) { const userdate = await query( sqlClientPool, - Sql.selectLastAccessed(user.id) + Sql.selectLastAccessed(user.id), ); if (userdate.length) { - user.lastAccessed = userdate[0].lastAccessed + user.lastAccessed = userdate[0].lastAccessed; } - let userFed = await fetchFederatedIdentities(user); + const userFed = await fetchFederatedIdentities(user); if (userFed.hasIdentities) { - user.isFederatedUser = userFed.hasIdentities + user.isFederatedUser = userFed.hasIdentities; } usersWithGitlabIdFetch.push({ ...user, - gitlabId: userFed.gitlabId + gitlabId: userFed.gitlabId, }); } @@ -258,7 +253,7 @@ export const User = (clients: { const linkUserToGitlab = async ( user: User, - gitlabUserId: string + gitlabUserId: string, ): Promise => { try { // Add Gitlab Federated Identity to User @@ -268,12 +263,12 @@ export const User = (clients: { federatedIdentity: { identityProvider: 'gitlab', userId: gitlabUserId, - userName: gitlabUserId // we don't map the username, instead just use the UID again - } + userName: gitlabUserId, // we don't map the username, instead just use the UID again + }, }); } catch (err) { throw new Error( - `Error linking user "${user.email}" to Gitlab Federated Identity: ${err}` + `Error linking user "${user.email}" to Gitlab Federated Identity: ${err}`, ); } }; @@ -283,41 +278,41 @@ export const User = (clients: { // Remove Gitlab Federated Identity from User await keycloakAdminClient.users.delFromFederatedIdentity({ id: user.id, - federatedIdentityId: 'gitlab' + federatedIdentityId: 'gitlab', }); } catch (err) { if (err.response.status && err.response.status === 404) { // No-op } else { throw new Error( - `Error unlinking user "${user.email}" from Gitlab Federated Identity: ${err}` + `Error unlinking user "${user.email}" from Gitlab Federated Identity: ${err}`, ); } } }; const loadUserById = async (id: string): Promise => { - let keycloakUser: User + let keycloakUser: User; keycloakUser = await keycloakAdminClient.users.findOne({ - id + id, }); const users = await transformKeycloakUsers([keycloakUser]); - keycloakUser = users[0] + keycloakUser = users[0]; if (R.isNil(keycloakUser)) { throw new UserNotFoundError(`User not found a: ${id}`); } // fetch the user's optin-in preference for organization emails - var emailOptions = { + let emailOptions = { organizationRoleChanges: false, sshKeyChanges: false, - groupRoleChanges: false + groupRoleChanges: false, }; try { const userOptIn = await query( sqlClientPool, - Sql.selectUserById(id) + Sql.selectUserById(id), ); if (userOptIn.length) { emailOptions = { @@ -342,7 +337,7 @@ export const User = (clients: { email = email.toLocaleLowerCase(); const keycloakUsers = await keycloakAdminClient.users.find({ briefRepresentation: false, - email + email, }); if (R.isEmpty(keycloakUsers)) { @@ -350,8 +345,8 @@ export const User = (clients: { } const userId = R.pipe( - R.filter(R.propEq('email', email)), - R.path(['0', 'id']) + R.filter(R.propEq(email, 'email')), + R.path(['0', 'id']), )(keycloakUsers); if (R.isNil(userId)) { @@ -378,15 +373,15 @@ export const User = (clients: { const loadAllKeycloakUsers = async (): Promise => { let users = []; let first = 0; - let pageSize = 200; + const pageSize = 200; let fetchedUsers; while (true) { fetchedUsers = await keycloakAdminClient.users.find({ briefRepresentation: false, - first: first, - max: pageSize + first, + max: pageSize, }); - users = users.concat(fetchedUsers) + users = users.concat(fetchedUsers); first += pageSize; if (fetchedUsers.length < pageSize) { break; @@ -428,23 +423,23 @@ export const User = (clients: { return false; }; - let keycloakUsers = await loadAllKeycloakUsers() + const keycloakUsers = await loadAllKeycloakUsers(); - let filteredOwners = filterUsersByAttribute(keycloakUsers, ownerFilter); - let filteredAdmins = filterUsersByAttribute(keycloakUsers, adminFilter); - let filteredViewers = filterUsersByAttribute(keycloakUsers, viewerFilter); + const filteredOwners = filterUsersByAttribute(keycloakUsers, ownerFilter); + const filteredAdmins = filterUsersByAttribute(keycloakUsers, adminFilter); + const filteredViewers = filterUsersByAttribute(keycloakUsers, viewerFilter); for (const f1 in filteredOwners) { - filteredOwners[f1].owner = true - filteredOwners[f1].organizationRole = "OWNER" + filteredOwners[f1].owner = true; + filteredOwners[f1].organizationRole = 'OWNER'; } for (const f1 in filteredAdmins) { - filteredAdmins[f1].admin = true - filteredAdmins[f1].organizationRole = "ADMIN" + filteredAdmins[f1].admin = true; + filteredAdmins[f1].organizationRole = 'ADMIN'; } for (const f1 in filteredViewers) { - filteredViewers[f1].organizationRole = "VIEWER" + filteredViewers[f1].organizationRole = 'VIEWER'; } - const orgUsers = [...filteredOwners, ...filteredAdmins, ...filteredViewers] + const orgUsers = [...filteredOwners, ...filteredAdmins, ...filteredViewers]; const users = await transformKeycloakUsers(orgUsers); @@ -452,53 +447,53 @@ export const User = (clients: { }; const loadAllUsers = async (): Promise => { - let users = await loadAllKeycloakUsers() - const keycloakUsers = await transformKeycloakUsers(users) + const users = await loadAllKeycloakUsers(); + const keycloakUsers = await transformKeycloakUsers(users); return keycloakUsers; }; const loadAllPlatformUsers = async (): Promise => { - let platformUsers = []; + const platformUsers = []; const keycloakPlatformOwners = await keycloakAdminClient.roles.findUsersWithRole({ name: PlatformRole.OWNER, - max: -1 + max: -1, }); for (const f1 in keycloakPlatformOwners) { - keycloakPlatformOwners[f1].platformRoles = [] + keycloakPlatformOwners[f1].platformRoles = []; const found = platformUsers.findIndex(el => el.email === keycloakPlatformOwners[f1].email); if (found === -1) { - keycloakPlatformOwners[f1].platformRoles.push(PlatformRole.OWNER) - platformUsers.push(keycloakPlatformOwners[f1]) + keycloakPlatformOwners[f1].platformRoles.push(PlatformRole.OWNER); + platformUsers.push(keycloakPlatformOwners[f1]); } else { - platformUsers[found].platformRoles.push(PlatformRole.OWNER) + platformUsers[found].platformRoles.push(PlatformRole.OWNER); } } const keycloakPlatformViewers = await keycloakAdminClient.roles.findUsersWithRole({ name: PlatformRole.VIEWER, - max: -1 + max: -1, }); for (const f1 in keycloakPlatformViewers) { - keycloakPlatformViewers[f1].platformRoles = [] + keycloakPlatformViewers[f1].platformRoles = []; const found = platformUsers.findIndex(el => el.email === keycloakPlatformViewers[f1].email); if (found === -1) { - keycloakPlatformViewers[f1].platformRoles.push(PlatformRole.VIEWER) - platformUsers.push(keycloakPlatformViewers[f1]) + keycloakPlatformViewers[f1].platformRoles.push(PlatformRole.VIEWER); + platformUsers.push(keycloakPlatformViewers[f1]); } else { - platformUsers[found].platformRoles.push(PlatformRole.VIEWER) + platformUsers[found].platformRoles.push(PlatformRole.VIEWER); } } const keycloakPlatformOrgOwners = await keycloakAdminClient.roles.findUsersWithRole({ name: PlatformRole.ORGANIZATION_OWNER, - max: -1 + max: -1, }); for (const f1 in keycloakPlatformOrgOwners) { - keycloakPlatformOrgOwners[f1].platformRoles = [] + keycloakPlatformOrgOwners[f1].platformRoles = []; const found = platformUsers.findIndex(el => el.email === keycloakPlatformOrgOwners[f1].email); if (found === -1) { - keycloakPlatformOrgOwners[f1].platformRoles.push(PlatformRole.ORGANIZATION_OWNER) - platformUsers.push(keycloakPlatformOrgOwners[f1]) + keycloakPlatformOrgOwners[f1].platformRoles.push(PlatformRole.ORGANIZATION_OWNER); + platformUsers.push(keycloakPlatformOrgOwners[f1]); } else { - platformUsers[found].platformRoles.push(PlatformRole.ORGANIZATION_OWNER) + platformUsers[found].platformRoles.push(PlatformRole.ORGANIZATION_OWNER); } } const users = await transformKeycloakUsers(platformUsers); @@ -507,31 +502,31 @@ export const User = (clients: { const addPlatformRoleToUser = async (userInput: User, role: PlatformRole): Promise => { const kcRole = await keycloakAdminClient.roles.findOneByName({ - name: role + name: role, }); - let keycloakUser = await keycloakAdminClient.users.addRealmRoleMappings({ + const keycloakUser = await keycloakAdminClient.users.addRealmRoleMappings({ id: userInput.id, roles: [{ id: kcRole.id, - name: kcRole.name - }] + name: kcRole.name, + }], }); - return keycloakUser - } + return keycloakUser; + }; const removePlatformRoleFromUser = async (userInput: User, role: PlatformRole): Promise => { const kcRole = await keycloakAdminClient.roles.findOneByName({ - name: role + name: role, }); const keycloakUser = await keycloakAdminClient.users.delRealmRoleMappings({ id: userInput.id, roles: [{ id: kcRole.id, - name: kcRole.name - }] + name: kcRole.name, + }], }); - return keycloakUser - } + return keycloakUser; + }; const getAllGroupsForUser = async ( userId: string, @@ -542,14 +537,14 @@ export const User = (clients: { id: userId, briefRepresentation: false, })) as KeycloakLagoonGroup[]; - let roleSubgroups = []; - for (const keycloakGroup of keycloakGroups){ + const roleSubgroups = []; + for (const keycloakGroup of keycloakGroups) { const projectIds = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(keycloakGroup.id); const organizationId = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(keycloakGroup.id); - roleSubgroups.push(GroupModel.createGroupFromKeycloak(keycloakGroup, projectIds, organizationId)) + roleSubgroups.push(GroupModel.createGroupFromKeycloak(keycloakGroup, projectIds, organizationId)); } - let userGroups: { + const userGroups: { [key: string]: Group; } = {}; for (const ug of roleSubgroups) { @@ -560,7 +555,7 @@ export const User = (clients: { if (!userGroups[ug.parentGroupId]) { const parentGroup = await GroupModel.loadGroupById(ug.parentGroupId); const parentOrg = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(parentGroup.id); - if (organization && parentOrg && toNumber(parentOrg) != organization) { + if (organization && parentOrg && toNumber(parentOrg) !== organization) { continue; } // only set the users role-group as the subgroup, this is because @@ -576,81 +571,80 @@ export const User = (clients: { return Object.values(userGroups); }; -const getAllProjectsIdsForUser = async ( - userId: string, - groups?: Group[] -): Promise => { - const GroupModel = Group(clients); - let userGroups = []; - if (!groups) { - groups = await keycloakAdminClient.users.listGroups({ - id: userId, - briefRepresentation: false - }); - } + const getAllProjectsIdsForUser = async ( + userId: string, + groups?: Group[], + ): Promise => { + const GroupModel = Group(clients); + const userGroups = []; + if (!groups) { + groups = await keycloakAdminClient.users.listGroups({ + id: userId, + briefRepresentation: false, + }); + } - const regexp = /-(owner|maintainer|developer|reporter|guest)$/g; - for (const ug of groups) { + const regexp = /-(owner|maintainer|developer|reporter|guest)$/g; + for (const ug of groups) { // push the group ids into an array of group ids only for sql lookups - let index = userGroups.findIndex((item: any) => item.name === ug.name.replace(regexp, "")); - if (index === -1) { - const parentGroup = await GroupModel.loadGroupByName(ug.name.replace(regexp, "")) - userGroups.push(parentGroup); + const index = userGroups.findIndex((item: any) => item.name === ug.name.replace(regexp, '')); + if (index === -1) { + const parentGroup = await GroupModel.loadGroupByName(ug.name.replace(regexp, '')); + userGroups.push(parentGroup); + } } - } - let roleProjectIds: any = {}; - for (const roleSubgroup of userGroups as Group[]) { - for (const fullSubgroup of groups as Group[]) { - for (const group of fullSubgroup.subGroups) { + const roleProjectIds: any = {}; + for (const roleSubgroup of userGroups as Group[]) { + for (const fullSubgroup of groups as Group[]) { + for (const group of fullSubgroup.subGroups) { // filter out the users roles subgroup from the main group so the correct roles are attached to the project ids - if (roleSubgroup.name.replace(regexp, "") == fullSubgroup.name) { + if (roleSubgroup.name.replace(regexp, '') === fullSubgroup.name) { // https://github.com/uselagoon/lagoon/pull/3358 references potential issue with the lagoon-projects attribute where there could be empty values // getProjectsFromGroupAndSubgroups already covers this fix - const projectIds = await GroupModel.getProjectsFromGroupAndSubgroups( - roleSubgroup - ); - if (!roleProjectIds[group.realmRoles[0]]) { - roleProjectIds[group.realmRoles[0]] = [] + const projectIds = await GroupModel.getProjectsFromGroupAndSubgroups( + roleSubgroup, + ); + if (!roleProjectIds[group.realmRoles[0]]) { + roleProjectIds[group.realmRoles[0]] = []; + } + projectIds.forEach((pid: any) => { + roleProjectIds[group.realmRoles[0]].indexOf(pid) === -1 ? roleProjectIds[group.realmRoles[0]].push(pid) : ''; + }); } - projectIds.forEach((pid: any) => { - roleProjectIds[group.realmRoles[0]].indexOf(pid) === -1 ? roleProjectIds[group.realmRoles[0]].push(pid) : "" - }) } } } - } - const allPidsSet = new Set(); - for (const roleKey in roleProjectIds) { - if (Object.prototype.hasOwnProperty.call(roleProjectIds, roleKey)) { - const pidsForRole = roleProjectIds[roleKey]; - if (Array.isArray(pidsForRole)) { - pidsForRole.forEach(pid => allPidsSet.add(pid)); + const allPidsSet = new Set(); + for (const roleKey in roleProjectIds) { + if (Object.prototype.hasOwnProperty.call(roleProjectIds, roleKey)) { + const pidsForRole = roleProjectIds[roleKey]; + if (Array.isArray(pidsForRole)) { + pidsForRole.forEach(pid => allPidsSet.add(pid)); + } } } - } - return roleProjectIds; -}; + return roleProjectIds; + }; const getUserRolesForProject = async ( userInput: User, projectId: number, - userGroups: Group[] + userGroups: Group[], ): Promise => { const GroupModel = Group(clients); let roles = []; for (const group of userGroups) { const projectIds = await GroupModel.getProjectsFromGroupAndSubgroups( - group + group, ); if (projectIds.includes(projectId)) { const groupRoles = R.pipe( - R.filter(membership => - R.pathEq(['user', 'id'], userInput.id, membership) + R.filter(membership => R.pathEq(userInput.id, ['user', 'id'], membership), ), - R.pluck('role') + R.pluck('role'), )(group.members); roles = [...roles, ...groupRoles]; @@ -660,23 +654,23 @@ const getAllProjectsIdsForUser = async ( return R.uniq(roles); }; - const addUser = async (userInput: User, resetPassword?: Boolean): Promise => { + const addUser = async (userInput: User, resetPassword?: boolean): Promise => { let response: { id: string }; try { response = await keycloakAdminClient.users.create({ ...pickNonNil( ['email', 'username', 'firstName', 'lastName'], - userInput + userInput, ), enabled: true, attributes: { - comment: [R.defaultTo('', R.prop('comment', userInput))] - } + comment: [R.defaultTo('', R.prop('comment', userInput))], + }, }); } catch (err) { if (err.response.status && err.response.status === 409) { throw new UsernameExistsError( - `Username ${R.prop('username', userInput)} exists` + `Username ${R.prop('username', userInput)} exists`, ); } else { throw new Error(`Error creating Lagoon user account: ${err.message}`); @@ -694,14 +688,14 @@ const getAllProjectsIdsForUser = async ( await keycloakAdminClient.users.executeActionsEmail({ id: user.id, lifespan: 43200, - actions: ["UPDATE_PASSWORD"], - clientId: "lagoon-ui", - redirectUri: getConfigFromEnv('UI_URL', "http://localhost:8888") + actions: ['UPDATE_PASSWORD'], + clientId: 'lagoon-ui', + redirectUri: getConfigFromEnv('UI_URL', 'http://localhost:8888'), }); } // Let's start with the email opt-in preference defaults - var emailOptinUpdates = { + const emailOptinUpdates = { opt_email_org_role: true, opt_email_sshkey: true, opt_email_group_role: false, @@ -711,10 +705,10 @@ const getAllProjectsIdsForUser = async ( if (userInput.emailNotifications.organizationRoleChanges !== undefined) { emailOptinUpdates.opt_email_org_role = userInput.emailNotifications.organizationRoleChanges; } - if (userInput.emailNotifications.sshKeyChanges !== undefined) { + if (userInput.emailNotifications.sshKeyChanges !== undefined) { emailOptinUpdates.opt_email_sshkey = userInput.emailNotifications.sshKeyChanges; } - if (userInput.emailNotifications.groupRoleChanges !== undefined) { + if (userInput.emailNotifications.groupRoleChanges !== undefined) { emailOptinUpdates.opt_email_group_role = userInput.emailNotifications.groupRoleChanges; } } @@ -746,40 +740,36 @@ const getAllProjectsIdsForUser = async ( }; }; - const removeOrgFromAttr = (attr, organization, user) => { - return R.pipe( - // @ts-ignore - R.view(attr), - R.defaultTo(`${organization}`), - R.split(','), - R.without(`${organization}`), - R.uniq, - R.join(',') - // @ts-ignore - )(user); - } + const removeOrgFromAttr = (attr, organization, user) => R.pipe( + // @ts-ignore + R.view(attr), + R.defaultTo(`${organization}`), + R.split(','), + R.without([`${organization}`]), + R.uniq, + R.join(','), + // @ts-ignore + )(user); - const addOrgToAttr = (attr, organization, user) => { - return R.pipe( - // @ts-ignore - R.view(attr), - R.defaultTo(`${organization}`), - R.split(','), - R.append(`${organization}`), - R.uniq, - R.join(',') - // @ts-ignore - )(user); - } + const addOrgToAttr = (attr, organization, user) => R.pipe( + // @ts-ignore + R.view(attr), + R.defaultTo(`${organization}`), + R.split(','), + R.append(`${organization}`), + R.uniq, + R.join(','), + // @ts-ignore + )(user); const resetUserPassword = async (id: string): Promise => { try { await keycloakAdminClient.users.executeActionsEmail({ - id: id, + id, lifespan: 43200, - actions: ["UPDATE_PASSWORD"], - clientId: "lagoon-ui", - redirectUri: getConfigFromEnv('UI_URL', "http://localhost:8888") + actions: ['UPDATE_PASSWORD'], + clientId: 'lagoon-ui', + redirectUri: getConfigFromEnv('UI_URL', 'http://localhost:8888'), }); } catch (err) { if (err.response.status && err.response.status === 404) { @@ -790,29 +780,29 @@ const getAllProjectsIdsForUser = async ( } }; - const userLastAccessed = async (userInput: User): Promise => { + const userLastAccessed = async (userInput: User): Promise => { // set the last accessed as a unix timestamp on the user attributes try { await query( sqlClientPool, - Sql.updateLastAccessed(userInput.id) + Sql.updateLastAccessed(userInput.id), ); } catch (err) { logger.warn(`Error updating user: ${err.message}`); } - return true + return true; }; - const getFullUserDetails = async (userInput: User): Promise => { + const getFullUserDetails = async (userInput: User): Promise => { // get the local DB user details const user = await query( sqlClientPool, - Sql.selectUserById(userInput.id) + Sql.selectUserById(userInput.id), ); return { ...user[0], - } - } + }; + }; const updateUser = async (userInput: UserEdit): Promise => { // update a users organization if required, hooks into the existing update user function, but is used by the addusertoorganization resolver @@ -830,36 +820,32 @@ const getAllProjectsIdsForUser = async ( email: curEmail, username: curUsername, firstName: curFirstName, - lastName: curLastName + lastName: curLastName, } = user; // set the comment if provided if (R.prop('comment', userInput)) { - comment = {comment: R.prop('comment', userInput)} + comment = { comment: R.prop('comment', userInput) }; } // set the organization if provided - if (R.prop('organization', userInput)) { + if (R.prop('organization' as any, userInput as any)) { // owner is an option, default is view if (R.prop('remove', userInput)) { - organizations = {'lagoon-organizations': [removeOrgFromAttr(attrLagoonOrgOwnerLens, R.prop('organization', userInput), user)]} - organizationsAdmin = {'lagoon-organizations-admin': [removeOrgFromAttr(attrLagoonOrgAdminLens, R.prop('organization', userInput), user)]} - organizationsView = {'lagoon-organizations-viewer': [removeOrgFromAttr(attrLagoonOrgViewerLens, R.prop('organization', userInput), user)]} + organizations = { 'lagoon-organizations': [removeOrgFromAttr(attrLagoonOrgOwnerLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsAdmin = { 'lagoon-organizations-admin': [removeOrgFromAttr(attrLagoonOrgAdminLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsView = { 'lagoon-organizations-viewer': [removeOrgFromAttr(attrLagoonOrgViewerLens, R.prop('organization' as any, userInput as any), user)] }; + } else if (R.prop('owner' as any, userInput as any)) { + organizations = { 'lagoon-organizations': [addOrgToAttr(attrLagoonOrgOwnerLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsAdmin = { 'lagoon-organizations-admin': [removeOrgFromAttr(attrLagoonOrgAdminLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsView = { 'lagoon-organizations-viewer': [removeOrgFromAttr(attrLagoonOrgViewerLens, R.prop('organization' as any, userInput as any), user)] }; + } else if (R.prop('admin' as any, userInput as any)) { + organizations = { 'lagoon-organizations': [removeOrgFromAttr(attrLagoonOrgOwnerLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsAdmin = { 'lagoon-organizations-admin': [addOrgToAttr(attrLagoonOrgAdminLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsView = { 'lagoon-organizations-viewer': [removeOrgFromAttr(attrLagoonOrgViewerLens, R.prop('organization' as any, userInput as any), user)] }; } else { - if (R.prop('owner', userInput)) { - organizations = {'lagoon-organizations': [addOrgToAttr(attrLagoonOrgOwnerLens, R.prop('organization', userInput), user)]} - organizationsAdmin = {'lagoon-organizations-admin': [removeOrgFromAttr(attrLagoonOrgAdminLens, R.prop('organization', userInput), user)]} - organizationsView = {'lagoon-organizations-viewer': [removeOrgFromAttr(attrLagoonOrgViewerLens, R.prop('organization', userInput), user)]} - } else { - if (R.prop('admin', userInput)) { - organizations = {'lagoon-organizations': [removeOrgFromAttr(attrLagoonOrgOwnerLens, R.prop('organization', userInput), user)]} - organizationsAdmin = {'lagoon-organizations-admin': [addOrgToAttr(attrLagoonOrgAdminLens, R.prop('organization', userInput), user)]} - organizationsView = {'lagoon-organizations-viewer': [removeOrgFromAttr(attrLagoonOrgViewerLens, R.prop('organization', userInput), user)]} - } else { - organizations = {'lagoon-organizations': [removeOrgFromAttr(attrLagoonOrgOwnerLens, R.prop('organization', userInput), user)]} - organizationsAdmin = {'lagoon-organizations-admin': [removeOrgFromAttr(attrLagoonOrgAdminLens, R.prop('organization', userInput), user)]} - organizationsView = {'lagoon-organizations-viewer': [addOrgToAttr(attrLagoonOrgViewerLens, R.prop('organization', userInput), user)]} - } - } + organizations = { 'lagoon-organizations': [removeOrgFromAttr(attrLagoonOrgOwnerLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsAdmin = { 'lagoon-organizations-admin': [removeOrgFromAttr(attrLagoonOrgAdminLens, R.prop('organization' as any, userInput as any), user)] }; + organizationsView = { 'lagoon-organizations-viewer': [addOrgToAttr(attrLagoonOrgViewerLens, R.prop('organization' as any, userInput as any), user)] }; } } @@ -868,12 +854,12 @@ const getAllProjectsIdsForUser = async ( const GroupModel = Group(clients); const userGroups = await getAllGroupsForUser(userInput.id); await Promise.all( - userGroups.map((userGroup) => GroupModel.purgeGroupCache(userGroup, true) ) + userGroups.map((userGroup) => GroupModel.purgeGroupCache(userGroup, true)), ); await keycloakAdminClient.users.update( { - id: userInput.id + id: userInput.id, }, { email: userInput.email ?? curEmail, @@ -885,9 +871,9 @@ const getAllProjectsIdsForUser = async ( ...organizations, ...organizationsAdmin, ...organizationsView, - ...comment - } - } + ...comment, + }, + }, ); } catch (err) { if (err.response.status && err.response.status === 404) { @@ -909,20 +895,20 @@ const getAllProjectsIdsForUser = async ( // TODO - this is probably a good place to put the updates generally // we need to build the update query - it'll consist of three different items - var emailOptinUpdates = {}; + let emailOptinUpdates = {}; if (userInput.emailNotifications !== undefined) { if (userInput.emailNotifications.organizationRoleChanges !== undefined) { - emailOptinUpdates = {opt_email_org_role: userInput.emailNotifications.organizationRoleChanges, ...emailOptinUpdates}; + emailOptinUpdates = { opt_email_org_role: userInput.emailNotifications.organizationRoleChanges, ...emailOptinUpdates }; } - if (userInput.emailNotifications.sshKeyChanges !== undefined) { - emailOptinUpdates = {opt_email_sshkey: userInput.emailNotifications.sshKeyChanges, ...emailOptinUpdates}; + if (userInput.emailNotifications.sshKeyChanges !== undefined) { + emailOptinUpdates = { opt_email_sshkey: userInput.emailNotifications.sshKeyChanges, ...emailOptinUpdates }; } - if (userInput.emailNotifications.groupRoleChanges !== undefined) { - emailOptinUpdates = {opt_email_group_role: userInput.emailNotifications.groupRoleChanges, ...emailOptinUpdates}; + if (userInput.emailNotifications.groupRoleChanges !== undefined) { + emailOptinUpdates = { opt_email_group_role: userInput.emailNotifications.groupRoleChanges, ...emailOptinUpdates }; } } - var successfulEmailPatch = {}; + let successfulEmailPatch = {}; try { if (emailOptinUpdates && Object.keys(emailOptinUpdates).length > 0) { await query( @@ -943,7 +929,7 @@ const getAllProjectsIdsForUser = async ( return { ...user, gitlabId: R.prop('gitlabId', userInput), - emailNotifications: {...user.emailNotifications, ...successfulEmailPatch}, + emailNotifications: { ...user.emailNotifications, ...successfulEmailPatch }, }; }; @@ -952,17 +938,17 @@ const getAllProjectsIdsForUser = async ( // delete the ssh keys of the user await query( sqlClientPool, - Sql.deleteFromSshKeys(id) + Sql.deleteFromSshKeys(id), ); // delete from the reference table await query( sqlClientPool, - Sql.deleteFromUserSshKeys(id) + Sql.deleteFromUserSshKeys(id), ); // delete from the user table await query( sqlClientPool, - Sql.deleteFromUser(id) + Sql.deleteFromUser(id), ); await keycloakAdminClient.users.del({ id }); @@ -976,31 +962,31 @@ const getAllProjectsIdsForUser = async ( }; const getAllOrganizationIdsForUser = async ( - userInput: UserEdit + userInput: UserEdit, ): Promise => { let organizations = []; const user = await loadUserById(userInput.id); - const usersOrgs = R.defaultTo('', R.prop('lagoon-organizations', user.attributes)).toString() - const usersOrgsAdmin = R.defaultTo('', R.prop('lagoon-organizations-admin', user.attributes)).toString() - const usersOrgsViewer = R.defaultTo('', R.prop('lagoon-organizations-viewer', user.attributes)).toString() + const usersOrgs = R.defaultTo('', R.prop('lagoon-organizations', user.attributes)).toString(); + const usersOrgsAdmin = R.defaultTo('', R.prop('lagoon-organizations-admin', user.attributes)).toString(); + const usersOrgsViewer = R.defaultTo('', R.prop('lagoon-organizations-viewer', user.attributes)).toString(); - if (usersOrgs != "" ) { + if (usersOrgs !== '') { const usersOrgsArr = usersOrgs.split(','); for (const userOrg of usersOrgsArr) { - organizations = [...organizations, userOrg] + organizations = [...organizations, userOrg]; } } - if (usersOrgsAdmin != "" ) { + if (usersOrgsAdmin !== '') { const usersOrgsArr = usersOrgsAdmin.split(','); for (const userOrg of usersOrgsArr) { - organizations = [...organizations, userOrg] + organizations = [...organizations, userOrg]; } } - if (usersOrgsViewer != "" ) { + if (usersOrgsViewer !== '') { const usersOrgsArr = usersOrgsViewer.split(','); for (const userOrg of usersOrgsArr) { - organizations = [...organizations, userOrg] + organizations = [...organizations, userOrg]; } } return R.uniq(organizations); diff --git a/services/api/src/newrelic.js b/services/api/src/newrelic.js index cfaf8705a5..0c86e250de 100644 --- a/services/api/src/newrelic.js +++ b/services/api/src/newrelic.js @@ -1,4 +1,3 @@ -'use strict' /** * New Relic agent configuration. * @@ -44,7 +43,7 @@ exports.config = { 'response.headers.authorization', 'response.headers.proxyAuthorization', 'response.headers.setCookie*', - 'response.headers.x*' - ] - } -} + 'response.headers.x*', + ], + }, +}; diff --git a/services/api/src/requestMiddleware.ts b/services/api/src/requestMiddleware.ts index 3cc510dd83..246b592639 100644 --- a/services/api/src/requestMiddleware.ts +++ b/services/api/src/requestMiddleware.ts @@ -10,7 +10,7 @@ export type RequestWithRequestData = Request & { const ipAddress = async ( req: RequestWithRequestData, _res: Response, - next: NextFunction + next: NextFunction, ) => { try { const ipAddress = getClientIp(req); diff --git a/services/api/src/resolvers.js b/services/api/src/resolvers.js index 98ed8decff..3fecef7772 100644 --- a/services/api/src/resolvers.js +++ b/services/api/src/resolvers.js @@ -7,7 +7,7 @@ const { addProblem, deleteProblem, deleteProblemsFromSource, - addProblemsFromSource, + _addProblemsFromSource, getProblemSources, } = require('./resources/problem/resolvers'); @@ -124,10 +124,10 @@ const { updateEnvironment, getAllEnvironments, userCanSshToEnvironment, - getEnvironmentUrl, + _getEnvironmentUrl, getEnvironmentsByKubernetes, addOrUpdateEnvironmentService, - getEnvironmentByServiceId, + _getEnvironmentByServiceId, getServiceContainersByServiceId, deleteEnvironmentService, getBytesUsed, @@ -137,7 +137,7 @@ const { const { getPendingChangesByEnvironmentId, -} = require('./resources/environment/environment_redeploy') +} = require('./resources/environment/environment_redeploy'); const { getDeployTargetConfigById, @@ -196,12 +196,12 @@ const { getProjectsByMetadata, getAllProjects, updateProject, - getProjectUrl, + _getProjectUrl, updateProjectMetadata, removeProjectMetadataByKey, getPrivateKey, getProjectDeployKey, - getFeatureApiRoutes + getFeatureApiRoutes, } = require('./resources/project/resolvers'); const { @@ -280,7 +280,7 @@ const { removeUserFromOrganizationGroups, checkBulkImportProjectsAndGroupsToOrganization, bulkImportProjectsAndGroupsToOrganization, - getOrganizationByProject + getOrganizationByProject, } = require('./resources/organization/resolvers'); const { @@ -293,7 +293,7 @@ const { deleteRestore, getBackupDownloadLinkByBackupId, backupSubscriber, - getRestoreLocation, + _getRestoreLocation, } = require('./resources/backup/resolvers'); const { @@ -354,19 +354,18 @@ const { async function getResolvers() { let graphqlUpload; try { - const { default: GraphQLUpload } = await import("graphql-upload/GraphQLUpload.mjs"); + const { default: GraphQLUpload } = await import('graphql-upload/GraphQLUpload.mjs'); graphqlUpload = { ...GraphQLUpload, - parseLiteral: (ast) => { - return null; // Allow literals but treats them as null - issue with graphql-upload & apollo-server3 - } + parseLiteral: (_ast) => null, // Allow literals but treats them as null - issue with graphql-upload & apollo-server3 + }; if (!graphqlUpload) { throw new Error("Failed to load GraphQLUpload from 'graphql-upload'"); } } catch (error) { - console.error("Failed to load GraphQLUpload:", error); + console.error('Failed to load GraphQLUpload:', error); throw error; } @@ -399,51 +398,51 @@ async function getResolvers() { REPORTER: 'reporter', DEVELOPER: 'developer', MAINTAINER: 'maintainer', - OWNER: 'owner' + OWNER: 'owner', }, DeploymentSourceType: { API: 'api', - WEBHOOK: 'webhook' + WEBHOOK: 'webhook', }, DeploymentBuildType: { BUILD: 'build', - VARIABLES: 'variables' + VARIABLES: 'variables', }, TaskSourceType: { API: 'api', }, ProjectOrderType: { NAME: 'name', - CREATED: 'created' + CREATED: 'created', }, EnvOrderType: { NAME: 'name', - UPDATED: 'updated' + UPDATED: 'updated', }, DeployType: { BRANCH: 'branch', PULLREQUEST: 'pullrequest', - PROMOTE: 'promote' + PROMOTE: 'promote', }, EnvType: { PRODUCTION: 'production', - DEVELOPMENT: 'development' + DEVELOPMENT: 'development', }, EnvVariableType: { PROJECT: 'project', - ENVIRONMENT: 'environment' + ENVIRONMENT: 'environment', }, EnvVariableScope: { BUILD: 'build', RUNTIME: 'runtime', GLOBAL: 'global', CONTAINER_REGISTRY: 'container_registry', - INTERNAL_CONTAINER_REGISTRY: 'internal_container_registry' + INTERNAL_CONTAINER_REGISTRY: 'internal_container_registry', }, RestoreStatusType: { PENDING: 'pending', SUCCESSFUL: 'successful', - FAILED: 'failed' + FAILED: 'failed', }, DeploymentStatusType: { NEW: 'new', @@ -464,7 +463,7 @@ async function getResolvers() { }, NotificationContentType: { DEPLOYMENT: 'deployment', - PROBLEM: 'problem' + PROBLEM: 'problem', }, TaskStatusType: { NEW: 'new', @@ -497,10 +496,6 @@ async function getResolvers() { DAYS: 'days', MONTHS: 'months', }, - RetentionPolicyType: { - HARBOR: 'harbor', - HISTORY: 'history', - }, RouteSource: { API: 'api', YAML: 'yaml', @@ -559,12 +554,12 @@ async function getResolvers() { pathRoutes: getAutogeneratedPathRoutes, }, GroupInterface: { - __resolveType(group) { + __resolveType(_group) { return 'Group'; }, }, OrgGroupInterface: { - __resolveType(group) { + __resolveType(_group) { return 'OrgGroup'; }, }, @@ -656,288 +651,288 @@ async function getResolvers() { Task: { environment: getEnvironmentByTaskId, files: getFilesByTaskId, - logs: getTaskLog + logs: getTaskLog, }, File: { - download: getDownloadLink + download: getDownloadLink, }, Notification: { - __resolveType(obj) { - switch (obj.type) { - case 'slack': - return 'NotificationSlack'; - case 'rocketchat': - return 'NotificationRocketChat'; - case 'microsoftteams': - return 'NotificationMicrosoftTeams'; - case 'email': - return 'NotificationEmail'; - case 'webhook': - return 'NotificationWebhook'; - default: - return null; - } - } - }, - RetentionPolicy: { - __resolveType(obj) { - switch (obj.type) { - case 'harbor': - return 'HarborRetentionPolicy'; - case 'history': - return 'HistoryRetentionPolicy'; - default: - return null; - } - } - }, - AdvancedTaskDefinition: { - __resolveType (obj) { - switch(obj.type) { - case 'IMAGE': - return 'AdvancedTaskDefinitionImage'; - case 'COMMAND': - return 'AdvancedTaskDefinitionCommand'; - default: - return null; - } - } - }, - User: { - has2faEnabled: getUser2fa, - sshKeys: getUserSshKeys, - groups: getGroupsByUserId, - groupRoles: getGroupRolesByUserId, - }, - OrgUser: { - has2faEnabled: getUser2fa, - groupRoles: getGroupRolesByUserIdAndOrganization, - }, - Backup: { - restore: getRestoreByBackupId, - environment: getEnvironmentByBackupId - }, - Query: { - me: getMe, - lagoonVersion: getLagoonVersion, - userBySshKey: getUserBySshKey, - userBySshFingerprint: getUserBySshFingerprint, - projectByGitUrl: getProjectByGitUrl, - projectByName: getProjectByName, - orgProjectByName: getProjectByName, - environmentsByKubernetes: getEnvironmentsByKubernetes, - groupByName: getGroupByName, - groupByNameAndOrganization: getGroupsByNameAndOrganizationId, - usersByOrganization: getUsersByOrganizationId, - userByEmailAndOrganization: getUserByEmailAndOrganizationId, - problemSources: getProblemSources, - environmentByName: getEnvironmentByName, - environmentById: getEnvironmentById, - environmentByOpenshiftProjectName: getEnvironmentByOpenshiftProjectName, - environmentByKubernetesNamespaceName: getEnvironmentByKubernetesNamespaceName, - environmentsByFactSearch: getEnvironmentsByFactSearch, - userCanSshToEnvironment, - deploymentByRemoteId: getDeploymentByRemoteId, - deploymentByName: getDeploymentByName, - deploymentsByBulkId: getDeploymentsByBulkId, - deploymentsByFilter: getDeploymentsByFilter, - taskByTaskName: getTaskByTaskName, - taskByRemoteId: getTaskByRemoteId, - taskById: getTaskById, - advancedTaskDefinitionById, - advancedTasksForEnvironment: resolveTasksForEnvironment, - allAdvancedTaskDefinitions, - advancedTaskDefinitionArgumentById, - allProjects: getAllProjects, - allOpenshifts: getAllOpenshifts, - allKubernetes: getAllOpenshifts, - allEnvironments: getAllEnvironments, - allProblems: getAllProblems, - allGroups: getAllGroups, - allProjectsInGroup: getAllProjectsInGroup, - allUsers: getAllUsers, - allNotifications: getAllNotifications, - userByEmail: getUserByEmail, - projectsByMetadata: getProjectsByMetadata, - projectsByFactSearch: getProjectsByFactSearch, - deployTargetConfigById: getDeployTargetConfigById, - deployTargetConfigsByProjectId: getDeployTargetConfigsByProjectId, - deployTargetConfigsByDeployTarget: getDeployTargetConfigsByDeployTarget, - allOrganizations: getAllOrganizations, - organizationById: getOrganizationById, - organizationByName: getOrganizationByName, - getGroupProjectOrganizationAssociation, - getProjectGroupOrganizationAssociation, - getEnvVariablesByProjectEnvironmentName, - checkBulkImportProjectsAndGroupsToOrganization, - allPlatformUsers: getAllPlatformUsers, - getAuditLogs, - listAllRetentionPolicies, - getBackupDownloadLinkByBackupId, - getDownloadLinkByTaskFileId, - getTaskFileUploadForm, - }, - Mutation: { - addProblem, - deleteProblem, - deleteProblemsFromSource, - addFact, - addFacts, - addFactsByName, - deleteFact, - deleteFactsFromSource, - addFactReference, - deleteFactReference, - deleteAllFactReferencesByFactId, - addOrUpdateEnvironment, - updateEnvironment, - deleteEnvironment, - addOrUpdateEnvironmentStorage, - addOrUpdateStorageOnEnvironment, - addNotificationSlack, - updateNotificationSlack, - deleteNotificationSlack, - addNotificationWebhook, - updateNotificationWebhook, - deleteNotificationWebhook, - addNotificationRocketChat, - updateNotificationRocketChat, - deleteNotificationRocketChat, - addNotificationMicrosoftTeams, - updateNotificationMicrosoftTeams, - deleteNotificationMicrosoftTeams, - addNotificationEmail, - updateNotificationEmail, - deleteNotificationEmail, - addNotificationToProject, - removeNotificationFromProject, - addOpenshift, - updateOpenshift, - deleteOpenshift, - addKubernetes: addOpenshift, - updateKubernetes: updateOpenshift, - deleteKubernetes: deleteOpenshift, - addProject, - updateProject, - deleteProject, - updateProjectMetadata, - removeProjectMetadataByKey, - addSshKey, - updateSshKey, - deleteSshKey, - deleteSshKeyById, - addUserSSHPublicKey: addSshKey, - updateUserSSHPublicKey: updateSshKey, - deleteUserSSHPublicKey: deleteSshKeyById, - addUser, - updateUser, - addUserToOrganization, - removeUserFromOrganization, - addAdminToOrganization, - removeAdminFromOrganization, - resetUserPassword, - deleteUser, - addDeployment, - deleteDeployment, - updateDeployment, - cancelDeployment, - bulkDeployEnvironmentLatest, - addBackup, - deleteBackup, - addRestore, - updateRestore, - deleteRestore, - addEnvVariable, - deleteEnvVariable, - addOrUpdateEnvVariableByName, - deleteEnvVariableByName, - addTask, - addAdvancedTaskDefinition, - updateAdvancedTaskDefinition, - deleteAdvancedTaskDefinition, - invokeRegisteredTask, - taskDrushArchiveDump, - taskDrushSqlDump, - taskDrushCacheClear, - taskDrushCron, - taskDrushSqlSync, - taskDrushRsyncFiles, - taskDrushUserLogin, - deleteTask, - updateTask, - cancelTask, - setEnvironmentServices, - uploadFilesForTask, - deleteFilesForTask, - deployEnvironmentLatest, - deployEnvironmentBranch, - deployEnvironmentPullrequest, - deployEnvironmentPromote, - switchActiveStandby, - addGroup, - updateGroup, - deleteGroup, - addUserToGroup, - removeUserFromGroup, - addGroupsToProject, - removeGroupsFromProject, - addDeployTargetConfig, - deleteDeployTargetConfig, - updateDeployTargetConfig, - addOrganization, - updateOrganization, - deleteOrganization, - addGroupToOrganization: addGroup, - addExistingGroupToOrganization, - addExistingProjectToOrganization, - removeProjectFromOrganization, - addDeployTargetToOrganization, - removeDeployTargetFromOrganization, - updateEnvironmentDeployTarget, - removeUserFromOrganizationGroups, - bulkImportProjectsAndGroupsToOrganization, - addOrUpdateEnvironmentService, - deleteEnvironmentService, - addPlatformRoleToUser, - removePlatformRoleFromUser, - createHarborRetentionPolicy, - updateHarborRetentionPolicy, - deleteHarborRetentionPolicy, - addHarborRetentionPolicyLink, - removeHarborRetentionPolicyLink, - createHistoryRetentionPolicy, - updateHistoryRetentionPolicy, - deleteHistoryRetentionPolicy, - addHistoryRetentionPolicyLink, - removeHistoryRetentionPolicyLink, - addRouteToProject, - addOrUpdateRouteOnEnvironment, - updateRouteOnProject, - activeStandbyRouteMove, - removeRouteFromEnvironment, - addRouteAlternativeDomains, - removeRouteAlternativeDomain, - deleteRoute, - addRouteAnnotation, - removeRouteAnnotation, - addPathRoutesToRoute, - removePathRouteFromRoute, - updateAutogeneratedRouteConfigOnProject, - updateAutogeneratedRouteConfigOnEnvironment, - removeAutogeneratedRouteConfigFromProject, - removeAutogeneratedRouteConfigFromEnvironment, - idleOrUnidleEnvironment, - stopOrStartEnvironmentService - }, - Subscription: { - backupChanged: backupSubscriber, - deploymentChanged: deploymentSubscriber, - taskChanged: taskSubscriber - }, - Date: GraphQLDate, - JSON: GraphQLJSON, - SeverityScore: SeverityScoreType -}; + __resolveType(obj) { + switch (obj.type) { + case 'slack': + return 'NotificationSlack'; + case 'rocketchat': + return 'NotificationRocketChat'; + case 'microsoftteams': + return 'NotificationMicrosoftTeams'; + case 'email': + return 'NotificationEmail'; + case 'webhook': + return 'NotificationWebhook'; + default: + return null; + } + }, + }, + RetentionPolicy: { + __resolveType(obj) { + switch (obj.type) { + case 'harbor': + return 'HarborRetentionPolicy'; + case 'history': + return 'HistoryRetentionPolicy'; + default: + return null; + } + }, + }, + AdvancedTaskDefinition: { + __resolveType(obj) { + switch (obj.type) { + case 'IMAGE': + return 'AdvancedTaskDefinitionImage'; + case 'COMMAND': + return 'AdvancedTaskDefinitionCommand'; + default: + return null; + } + }, + }, + User: { + has2faEnabled: getUser2fa, + sshKeys: getUserSshKeys, + groups: getGroupsByUserId, + groupRoles: getGroupRolesByUserId, + }, + OrgUser: { + has2faEnabled: getUser2fa, + groupRoles: getGroupRolesByUserIdAndOrganization, + }, + Backup: { + restore: getRestoreByBackupId, + environment: getEnvironmentByBackupId, + }, + Query: { + me: getMe, + lagoonVersion: getLagoonVersion, + userBySshKey: getUserBySshKey, + userBySshFingerprint: getUserBySshFingerprint, + projectByGitUrl: getProjectByGitUrl, + projectByName: getProjectByName, + orgProjectByName: getProjectByName, + environmentsByKubernetes: getEnvironmentsByKubernetes, + groupByName: getGroupByName, + groupByNameAndOrganization: getGroupsByNameAndOrganizationId, + usersByOrganization: getUsersByOrganizationId, + userByEmailAndOrganization: getUserByEmailAndOrganizationId, + problemSources: getProblemSources, + environmentByName: getEnvironmentByName, + environmentById: getEnvironmentById, + environmentByOpenshiftProjectName: getEnvironmentByOpenshiftProjectName, + environmentByKubernetesNamespaceName: getEnvironmentByKubernetesNamespaceName, + environmentsByFactSearch: getEnvironmentsByFactSearch, + userCanSshToEnvironment, + deploymentByRemoteId: getDeploymentByRemoteId, + deploymentByName: getDeploymentByName, + deploymentsByBulkId: getDeploymentsByBulkId, + deploymentsByFilter: getDeploymentsByFilter, + taskByTaskName: getTaskByTaskName, + taskByRemoteId: getTaskByRemoteId, + taskById: getTaskById, + advancedTaskDefinitionById, + advancedTasksForEnvironment: resolveTasksForEnvironment, + allAdvancedTaskDefinitions, + advancedTaskDefinitionArgumentById, + allProjects: getAllProjects, + allOpenshifts: getAllOpenshifts, + allKubernetes: getAllOpenshifts, + allEnvironments: getAllEnvironments, + allProblems: getAllProblems, + allGroups: getAllGroups, + allProjectsInGroup: getAllProjectsInGroup, + allUsers: getAllUsers, + allNotifications: getAllNotifications, + userByEmail: getUserByEmail, + projectsByMetadata: getProjectsByMetadata, + projectsByFactSearch: getProjectsByFactSearch, + deployTargetConfigById: getDeployTargetConfigById, + deployTargetConfigsByProjectId: getDeployTargetConfigsByProjectId, + deployTargetConfigsByDeployTarget: getDeployTargetConfigsByDeployTarget, + allOrganizations: getAllOrganizations, + organizationById: getOrganizationById, + organizationByName: getOrganizationByName, + getGroupProjectOrganizationAssociation, + getProjectGroupOrganizationAssociation, + getEnvVariablesByProjectEnvironmentName, + checkBulkImportProjectsAndGroupsToOrganization, + allPlatformUsers: getAllPlatformUsers, + getAuditLogs, + listAllRetentionPolicies, + getBackupDownloadLinkByBackupId, + getDownloadLinkByTaskFileId, + getTaskFileUploadForm, + }, + Mutation: { + addProblem, + deleteProblem, + deleteProblemsFromSource, + addFact, + addFacts, + addFactsByName, + deleteFact, + deleteFactsFromSource, + addFactReference, + deleteFactReference, + deleteAllFactReferencesByFactId, + addOrUpdateEnvironment, + updateEnvironment, + deleteEnvironment, + addOrUpdateEnvironmentStorage, + addOrUpdateStorageOnEnvironment, + addNotificationSlack, + updateNotificationSlack, + deleteNotificationSlack, + addNotificationWebhook, + updateNotificationWebhook, + deleteNotificationWebhook, + addNotificationRocketChat, + updateNotificationRocketChat, + deleteNotificationRocketChat, + addNotificationMicrosoftTeams, + updateNotificationMicrosoftTeams, + deleteNotificationMicrosoftTeams, + addNotificationEmail, + updateNotificationEmail, + deleteNotificationEmail, + addNotificationToProject, + removeNotificationFromProject, + addOpenshift, + updateOpenshift, + deleteOpenshift, + addKubernetes: addOpenshift, + updateKubernetes: updateOpenshift, + deleteKubernetes: deleteOpenshift, + addProject, + updateProject, + deleteProject, + updateProjectMetadata, + removeProjectMetadataByKey, + addSshKey, + updateSshKey, + deleteSshKey, + deleteSshKeyById, + addUserSSHPublicKey: addSshKey, + updateUserSSHPublicKey: updateSshKey, + deleteUserSSHPublicKey: deleteSshKeyById, + addUser, + updateUser, + addUserToOrganization, + removeUserFromOrganization, + addAdminToOrganization, + removeAdminFromOrganization, + resetUserPassword, + deleteUser, + addDeployment, + deleteDeployment, + updateDeployment, + cancelDeployment, + bulkDeployEnvironmentLatest, + addBackup, + deleteBackup, + addRestore, + updateRestore, + deleteRestore, + addEnvVariable, + deleteEnvVariable, + addOrUpdateEnvVariableByName, + deleteEnvVariableByName, + addTask, + addAdvancedTaskDefinition, + updateAdvancedTaskDefinition, + deleteAdvancedTaskDefinition, + invokeRegisteredTask, + taskDrushArchiveDump, + taskDrushSqlDump, + taskDrushCacheClear, + taskDrushCron, + taskDrushSqlSync, + taskDrushRsyncFiles, + taskDrushUserLogin, + deleteTask, + updateTask, + cancelTask, + setEnvironmentServices, + uploadFilesForTask, + deleteFilesForTask, + deployEnvironmentLatest, + deployEnvironmentBranch, + deployEnvironmentPullrequest, + deployEnvironmentPromote, + switchActiveStandby, + addGroup, + updateGroup, + deleteGroup, + addUserToGroup, + removeUserFromGroup, + addGroupsToProject, + removeGroupsFromProject, + addDeployTargetConfig, + deleteDeployTargetConfig, + updateDeployTargetConfig, + addOrganization, + updateOrganization, + deleteOrganization, + addGroupToOrganization: addGroup, + addExistingGroupToOrganization, + addExistingProjectToOrganization, + removeProjectFromOrganization, + addDeployTargetToOrganization, + removeDeployTargetFromOrganization, + updateEnvironmentDeployTarget, + removeUserFromOrganizationGroups, + bulkImportProjectsAndGroupsToOrganization, + addOrUpdateEnvironmentService, + deleteEnvironmentService, + addPlatformRoleToUser, + removePlatformRoleFromUser, + createHarborRetentionPolicy, + updateHarborRetentionPolicy, + deleteHarborRetentionPolicy, + addHarborRetentionPolicyLink, + removeHarborRetentionPolicyLink, + createHistoryRetentionPolicy, + updateHistoryRetentionPolicy, + deleteHistoryRetentionPolicy, + addHistoryRetentionPolicyLink, + removeHistoryRetentionPolicyLink, + addRouteToProject, + addOrUpdateRouteOnEnvironment, + updateRouteOnProject, + activeStandbyRouteMove, + removeRouteFromEnvironment, + addRouteAlternativeDomains, + removeRouteAlternativeDomain, + deleteRoute, + addRouteAnnotation, + removeRouteAnnotation, + addPathRoutesToRoute, + removePathRouteFromRoute, + updateAutogeneratedRouteConfigOnProject, + updateAutogeneratedRouteConfigOnEnvironment, + removeAutogeneratedRouteConfigFromProject, + removeAutogeneratedRouteConfigFromEnvironment, + idleOrUnidleEnvironment, + stopOrStartEnvironmentService, + }, + Subscription: { + backupChanged: backupSubscriber, + deploymentChanged: deploymentSubscriber, + taskChanged: taskSubscriber, + }, + Date: GraphQLDate, + JSON: GraphQLJSON, + SeverityScore: SeverityScoreType, + }; return resolvers; } diff --git a/services/api/src/resources/audit/resolvers.ts b/services/api/src/resources/audit/resolvers.ts index c1a8418edb..be6d0e254e 100644 --- a/services/api/src/resources/audit/resolvers.ts +++ b/services/api/src/resources/audit/resolvers.ts @@ -1,12 +1,10 @@ -import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { knex, query } from '../../util/db'; - export const getAuditLogs: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { // default results limit to 100 let limit = 100; @@ -15,7 +13,7 @@ export const getAuditLogs: ResolverFn = async ( if (!adminScopes.platformOwner && !adminScopes.platformViewer) { if (input.organizationId) { await hasPermission('organization', 'view', { - organization: input.organizationId, + organization: input.organizationId, }); } else { await hasPermission('project', 'viewAll'); @@ -39,7 +37,7 @@ export const getAuditLogs: ResolverFn = async ( } // limit could be 0 - if (input.limit != undefined) { + if (input.limit !== undefined) { limit = input.limit; } @@ -60,7 +58,7 @@ export const getAuditLogs: ResolverFn = async ( } if (input.resourceDetails) { - queryBuilder = queryBuilder.where('resource_details','LIKE',`%${input.resourceDetails}%`); + queryBuilder = queryBuilder.where('resource_details', 'LIKE', `%${input.resourceDetails}%`); } if (input.linkedResourceId) { @@ -72,7 +70,7 @@ export const getAuditLogs: ResolverFn = async ( } if (input.linkedResourceDetails) { - queryBuilder = queryBuilder.where('linked_resource_details','LIKE',`%${input.linkedResourceDetails}%`); + queryBuilder = queryBuilder.where('linked_resource_details', 'LIKE', `%${input.linkedResourceDetails}%`); } if (input.auditEvent) { @@ -110,5 +108,5 @@ export const getAuditLogs: ResolverFn = async ( } const rows = await query(sqlClientPool, queryBuilder.toString()); - return rows -}; \ No newline at end of file + return rows; +}; diff --git a/services/api/src/resources/audit/sql.ts b/services/api/src/resources/audit/sql.ts index 991c668f7f..83b225b017 100644 --- a/services/api/src/resources/audit/sql.ts +++ b/services/api/src/resources/audit/sql.ts @@ -18,17 +18,14 @@ export interface AuditLog { organizationId?: number; } - export const Sql = { - insertAuditLog: (auditLog: AuditLog) => - knex('audit_log') - .insert(auditLog) - .toString(), - selectAuditLog: () => - knex('audit_log') - .toString(), - deleteAuditLog: (id: string) => - knex('audit_log') - .where('id', id) - .delete() - .toString(), - }; \ No newline at end of file +export const Sql = { + insertAuditLog: (auditLog: AuditLog) => knex('audit_log') + .insert(auditLog) + .toString(), + selectAuditLog: () => knex('audit_log') + .toString(), + deleteAuditLog: (id: string) => knex('audit_log') + .where('id', id) + .delete() + .toString(), +}; diff --git a/services/api/src/resources/audit/types.ts b/services/api/src/resources/audit/types.ts index f45c6a93aa..641d21bd3b 100644 --- a/services/api/src/resources/audit/types.ts +++ b/services/api/src/resources/audit/types.ts @@ -1,4 +1,4 @@ -import { AuditType } from "@lagoon/commons/src/types" +import { AuditType } from '../../commons/types'; export interface AuditLog { organizationId?: number @@ -10,4 +10,4 @@ export interface AuditResource { id?: string type: AuditType details?: string -} \ No newline at end of file +} diff --git a/services/api/src/resources/backup/resolvers.ts b/services/api/src/resources/backup/resolvers.ts index ff16cfc18f..93e6558eeb 100644 --- a/services/api/src/resources/backup/resolvers.ts +++ b/services/api/src/resources/backup/resolvers.ts @@ -1,25 +1,27 @@ import * as R from 'ramda'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; -import { createMiscTask } from '@lagoon/commons/dist/tasks'; -import { ResolverFn } from '../'; +import e from 'express'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; +import { createMiscTask } from '../../commons/tasks'; +import { ResolverFn } from '..'; import { getConfigFromEnv, s3Config } from '../../util/config'; import { query, isPatchEmpty, knex } from '../../util/db'; import { pubSub, createEnvironmentFilteredSubscriber, - EVENTS + EVENTS, } from '../../clients/pubSub'; import { Sql } from './sql'; import { Sql as projectSql } from '../project/sql'; import { Sql as environmentSql } from '../environment/sql'; import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; -import e from 'express'; import { logger } from '../../loggers/logger'; -const { S3Client, HeadObjectCommand, GetObjectCommand, DeleteObjectCommand } = require('@aws-sdk/client-s3'); +const { + S3Client, HeadObjectCommand, GetObjectCommand, DeleteObjectCommand, +} = require('@aws-sdk/client-s3'); const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); // https://{endpoint}/{bucket}/{key} @@ -27,93 +29,86 @@ const s3LinkMatch = /([^/]+)\/([^/]+)\/([^/]+)/; const awsLinkMatch = /s3\.([^.]+)\.amazonaws\.com\//; const getS3Client = async (sqlClientPool, projectId, s3Parts, restoreLocation) => { - let credentials = await getRestoreLocationS3Creds(sqlClientPool, projectId) + const credentials = await getRestoreLocationS3Creds(sqlClientPool, projectId); - let awsS3Parts; + let awsS3Parts; - if (R.test(awsLinkMatch, restoreLocation)) { - awsS3Parts = R.match(awsLinkMatch, restoreLocation); - } + if (R.test(awsLinkMatch, restoreLocation)) { + awsS3Parts = R.match(awsLinkMatch, restoreLocation); + } - // We have to generate a new client every time because the endpoint is parsed - // from the s3 url. - const URL = require('url').URL; - const restoreLocationURL = new URL(restoreLocation); - return new S3Client({ - credentials, - forcePathStyle: true, - signatureVersion: 'v4', - endpoint: `${restoreLocationURL.protocol}//${R.prop(1, s3Parts)}`, - region: awsS3Parts ? R.prop(1, awsS3Parts) : 'us-east-1' - }); -} + // We have to generate a new client every time because the endpoint is parsed + // from the s3 url. + const URL = require('url').URL; + const restoreLocationURL = new URL(restoreLocation); + return new S3Client({ + credentials, + forcePathStyle: true, + signatureVersion: 'v4', + endpoint: `${restoreLocationURL.protocol}//${R.prop(1, s3Parts)}`, + region: awsS3Parts ? R.prop(1, awsS3Parts) : 'us-east-1', + }); +}; const getRestoreLocationS3Creds = async (sqlClientPool, projectId) => { const projectEnvVars = await query(sqlClientPool, Sql.selectEnvVariablesByProjectsById(projectId)); // Handle custom restore configurations - let lagoonBaasCustomRestoreEndpoint = projectEnvVars.find(obj => { - return obj.name === "LAGOON_BAAS_CUSTOM_RESTORE_ENDPOINT" - }) + let lagoonBaasCustomRestoreEndpoint = projectEnvVars.find(obj => obj.name === 'LAGOON_BAAS_CUSTOM_RESTORE_ENDPOINT'); if (lagoonBaasCustomRestoreEndpoint) { - lagoonBaasCustomRestoreEndpoint = lagoonBaasCustomRestoreEndpoint.value + lagoonBaasCustomRestoreEndpoint = lagoonBaasCustomRestoreEndpoint.value; } - let lagoonBaasCustomRestoreBucket = projectEnvVars.find(obj => { - return obj.name === "LAGOON_BAAS_CUSTOM_RESTORE_BUCKET" - }) + let lagoonBaasCustomRestoreBucket = projectEnvVars.find(obj => obj.name === 'LAGOON_BAAS_CUSTOM_RESTORE_BUCKET'); if (lagoonBaasCustomRestoreBucket) { - lagoonBaasCustomRestoreBucket = lagoonBaasCustomRestoreBucket.value + lagoonBaasCustomRestoreBucket = lagoonBaasCustomRestoreBucket.value; } - let lagoonBaasCustomRestoreAccessKey = projectEnvVars.find(obj => { - return obj.name === "LAGOON_BAAS_CUSTOM_RESTORE_ACCESS_KEY" - }) + let lagoonBaasCustomRestoreAccessKey = projectEnvVars.find(obj => obj.name === 'LAGOON_BAAS_CUSTOM_RESTORE_ACCESS_KEY'); if (lagoonBaasCustomRestoreAccessKey) { - lagoonBaasCustomRestoreAccessKey = lagoonBaasCustomRestoreAccessKey.value + lagoonBaasCustomRestoreAccessKey = lagoonBaasCustomRestoreAccessKey.value; } - let lagoonBaasCustomRestoreSecretKey = projectEnvVars.find(obj => { - return obj.name === "LAGOON_BAAS_CUSTOM_RESTORE_SECRET_KEY" - }) + let lagoonBaasCustomRestoreSecretKey = projectEnvVars.find(obj => obj.name === 'LAGOON_BAAS_CUSTOM_RESTORE_SECRET_KEY'); if (lagoonBaasCustomRestoreSecretKey) { - lagoonBaasCustomRestoreSecretKey = lagoonBaasCustomRestoreSecretKey.value + lagoonBaasCustomRestoreSecretKey = lagoonBaasCustomRestoreSecretKey.value; } - let accessKeyId, secretAccessKey + let accessKeyId; let + secretAccessKey; if (lagoonBaasCustomRestoreEndpoint && lagoonBaasCustomRestoreBucket && lagoonBaasCustomRestoreAccessKey && lagoonBaasCustomRestoreSecretKey) { // Custom Restore location exists, use these credentials instead - accessKeyId = lagoonBaasCustomRestoreAccessKey - secretAccessKey = lagoonBaasCustomRestoreSecretKey + accessKeyId = lagoonBaasCustomRestoreAccessKey; + secretAccessKey = lagoonBaasCustomRestoreSecretKey; } else { // No Custom Restore location exists, use default credentials accessKeyId = getConfigFromEnv( 'S3_BAAS_ACCESS_KEY_ID', - 'XXXXXXXXXXXXXXXXXXXX' + 'XXXXXXXXXXXXXXXXXXXX', ); secretAccessKey = getConfigFromEnv( 'S3_BAAS_SECRET_ACCESS_KEY', - 'XXXXXXXXXXXXXXXXXXXX' + 'XXXXXXXXXXXXXXXXXXXX', ); } - return {accessKeyId, secretAccessKey}; -} + return { accessKeyId, secretAccessKey }; +}; const getRestoreLocation = async (backupId, restoreLocation, sqlClientPool, userActivityLogger, restoreSizeOnly = false) => { let restoreSize = 0; const rows = await query(sqlClientPool, Sql.selectBackupByBackupId(backupId)); - if (rows.length == 0) { - return + if (rows.length === 0) { + return; } const project = await projectHelpers(sqlClientPool).getProjectByEnvironmentId(rows[0].environment); if (R.test(s3LinkMatch, restoreLocation)) { const s3Parts = R.match(s3LinkMatch, restoreLocation); - const s3Client = await getS3Client(sqlClientPool, project.projectId, s3Parts, restoreLocation) - const bucket = R.prop(2, s3Parts) - const fileKey = R.prop(3, s3Parts) + const s3Client = await getS3Client(sqlClientPool, project.projectId, s3Parts, restoreLocation); + const bucket = R.prop(2, s3Parts); + const fileKey = R.prop(3, s3Parts); try { const headObjectResponse = await s3Client.send(new HeadObjectCommand({ Bucket: bucket, - Key: fileKey + Key: fileKey, })); restoreSize = headObjectResponse.ContentLength ?? restoreSize; @@ -121,7 +116,7 @@ const getRestoreLocation = async (backupId, restoreLocation, sqlClientPool, user Bucket: bucket, Key: fileKey, }), { - expiresIn: s3Config.signedLinkExpiration + expiresIn: s3Config.signedLinkExpiration, }); if (!restoreSizeOnly) { @@ -136,68 +131,66 @@ const getRestoreLocation = async (backupId, restoreLocation, sqlClientPool, user if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User requested a download link`, { + userActivityLogger('User requested a download link', { event: 'api:getSignedBackupUrl', payload: { Bucket: bucket, Key: fileKey, ...auditLog, - } + }, }); } } - return {restoreLocation: restLoc, restoreSize}; - } catch(err) { + return { restoreLocation: restLoc, restoreSize }; + } catch (_err) { await query( sqlClientPool, Sql.deleteRestore({ - backupId - }) + backupId, + }), ); - return {restoreLocation: "", restoreSize}; + return { restoreLocation: '', restoreSize }; } } - return {restoreLocation, restoreSize}; + return { restoreLocation, restoreSize }; }; const deleteRestoreLocation = async (backupId, restoreLocation, sqlClientPool) => { - let restoreSize = 0; const rows = await query(sqlClientPool, Sql.selectBackupByBackupId(backupId)); - if (rows.length == 0) { - return + if (rows.length === 0) { + return; } const project = await projectHelpers(sqlClientPool).getProjectByEnvironmentId(rows[0].environment); if (R.test(s3LinkMatch, restoreLocation)) { const s3Parts = R.match(s3LinkMatch, restoreLocation); - const s3Client = await getS3Client(sqlClientPool, project.projectId, s3Parts, restoreLocation) - const bucket = R.prop(2, s3Parts) - const fileKey = R.prop(3, s3Parts) + const s3Client = await getS3Client(sqlClientPool, project.projectId, s3Parts, restoreLocation); + const bucket = R.prop(2, s3Parts); + const fileKey = R.prop(3, s3Parts); try { - const headObjectResponse = await s3Client.send(new HeadObjectCommand({ + await s3Client.send(new HeadObjectCommand({ Bucket: bucket, - Key: fileKey + Key: fileKey, })); - restoreSize = headObjectResponse.ContentLength ?? restoreSize; // delete the object from the bucket await s3Client.send(new DeleteObjectCommand({ Bucket: bucket, - Key: fileKey + Key: fileKey, })); - } catch(err) { + } catch (_err) { // do nothing - logger.warn(`failed to check object reference in s3: ${e}`) + logger.warn(`failed to check object reference in s3: ${e}`); } // in any case, if the restore id is correct, delete the restore id await query( sqlClientPool, Sql.deleteRestore({ - backupId - }) + backupId, + }), ); } }; @@ -205,16 +198,16 @@ const deleteRestoreLocation = async (backupId, restoreLocation, sqlClientPool) = export const getBackupsByEnvironmentId: ResolverFn = async ( { id: environmentId }, { limit }, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('backup', 'view', { - project: environment.project + project: environment.project, }); } @@ -232,37 +225,41 @@ export const getBackupsByEnvironmentId: ResolverFn = async ( export const addBackup: ResolverFn = async ( root, - { input: { id, environment: environmentId, source, backupId, created } }, - { sqlClientPool, hasPermission, userActivityLogger } + { + input: { + id, environment: environmentId, source, backupId, created, + }, + }, + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); await hasPermission('backup', 'add', { - project: environment.project + project: environment.project, }); let insertId: number; try { - ({insertId} = await query( + ({ insertId } = await query( sqlClientPool, Sql.insertBackup({ id, environment: environmentId, source, backupId, - created - }) + created, + }), )); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding backup. Backup already exists.` + 'Error adding backup. Backup already exists.', ); } else { throw new Error(error.message); } - }; + } const rows = await query(sqlClientPool, Sql.selectBackup(insertId)); const backup = R.prop(0, rows); @@ -297,7 +294,7 @@ export const addBackup: ResolverFn = async ( backupId, created, ...auditLog, - } + }, }); return backup; @@ -306,15 +303,15 @@ export const addBackup: ResolverFn = async ( export const deleteBackup: ResolverFn = async ( root, { input: { backupId } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const perms = await query(sqlClientPool, Sql.selectPermsForBackup(backupId)); await hasPermission('backup', 'delete', { - project: R.path(['0', 'pid'], perms) + project: R.path(['0', 'pid'], perms), }); - const environment = await query(sqlClientPool, environmentSql.selectEnvironmentBySnapshotId(backupId)) + const environment = await query(sqlClientPool, environmentSql.selectEnvironmentBySnapshotId(backupId)); const project = await projectHelpers(sqlClientPool).getProjectById(environment[0].project); const rows = await query(sqlClientPool, Sql.selectBackupByBackupId(backupId)); const backup = R.prop(0, rows); @@ -341,7 +338,7 @@ export const deleteBackup: ResolverFn = async ( payload: { backupId, ...auditLog, - } + }, }); return 'success'; @@ -349,8 +346,14 @@ export const deleteBackup: ResolverFn = async ( export const addRestore: ResolverFn = async ( root, - { input: { id, backupId, status, restoreLocation, created, execute } }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + { + input: { + id, backupId, status, restoreLocation, created, execute, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { const perms = await query(sqlClientPool, Sql.selectPermsForBackup(backupId)); @@ -361,30 +364,30 @@ export const addRestore: ResolverFn = async ( } } await hasPermission('restore', 'add', { - project: R.path(['0', 'pid'], perms) + project: R.path(['0', 'pid'], perms), }); let insertId: number; try { - ({insertId} = await query( + ({ insertId } = await query( sqlClientPool, Sql.insertRestore({ id, backupId, status, restoreLocation, - created - }) + created, + }), )); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding restore. Restore already exists.` + 'Error adding restore. Restore already exists.', ); } else { throw new Error(error.message); } - }; + } let rows = await query(sqlClientPool, Sql.selectRestore(insertId)); const restoreData = R.prop(0, rows); @@ -398,23 +401,23 @@ export const addRestore: ResolverFn = async ( if (execute === false) { try { await hasPermission('restore', 'addNoExec', { - project: R.path(['0', 'pid'], perms) + project: R.path(['0', 'pid'], perms), }); return restoreData; - } catch (err) { + } catch (_err) { // Not allowed to stop execution. } } rows = await query( sqlClientPool, - environmentSql.selectEnvironmentById(backupData.environment) + environmentSql.selectEnvironmentById(backupData.environment), ); const environmentData = R.prop(0, rows); rows = await query( sqlClientPool, - projectSql.selectProject(environmentData.project) + projectSql.selectProject(environmentData.project), ); const projectData = R.prop(0, rows); @@ -422,7 +425,7 @@ export const addRestore: ResolverFn = async ( backup: backupData, restore: restoreData, environment: environmentData, - project: projectData + project: projectData, }; const auditLog: AuditLog = { @@ -449,7 +452,7 @@ export const addRestore: ResolverFn = async ( backupId, data, ...auditLog, - } + }, }); try { @@ -461,7 +464,7 @@ export const addRestore: ResolverFn = async ( '', 'api:addRestore', { restoreId: restoreData.id, backupId }, - `Restore not initiated, reason: ${error}` + `Restore not initiated, reason: ${error}`, ); } @@ -474,10 +477,12 @@ export const updateRestore: ResolverFn = async ( input: { backupId, patch, - patch: { status, created, restoreLocation } - } + patch: { status, created, restoreLocation }, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } ) => { if (isPatchEmpty({ patch })) { throw new Error('Input patch requires at least 1 attribute'); @@ -485,7 +490,7 @@ export const updateRestore: ResolverFn = async ( const permsRestore = await query( sqlClientPool, - Sql.selectPermsForRestore(backupId) + Sql.selectPermsForRestore(backupId), ); if (restoreLocation) { @@ -496,7 +501,7 @@ export const updateRestore: ResolverFn = async ( } // Check access to modify restore as it currently stands await hasPermission('restore', 'update', { - project: R.path(['0', 'pid'], permsRestore) + project: R.path(['0', 'pid'], permsRestore), }); await query( @@ -506,9 +511,9 @@ export const updateRestore: ResolverFn = async ( patch: { status, created, - restoreLocation - } - }) + restoreLocation, + }, + }), ); let rows = await query(sqlClientPool, Sql.selectRestoreByBackupId(backupId)); @@ -517,7 +522,7 @@ export const updateRestore: ResolverFn = async ( rows = await query(sqlClientPool, Sql.selectBackupByBackupId(backupId)); const backupData = R.prop(0, rows); - const environmentData = await query(sqlClientPool, environmentSql.selectEnvironmentBySnapshotId(backupId)) + const environmentData = await query(sqlClientPool, environmentSql.selectEnvironmentBySnapshotId(backupId)); const project = await projectHelpers(sqlClientPool).getProjectById(environmentData[0].project); pubSub.publish(EVENTS.BACKUP, backupData); @@ -537,62 +542,62 @@ export const updateRestore: ResolverFn = async ( if (project.organization) { auditLog.organizationId = project.organization; } - if (userActivityLogger != undefined) { + if (userActivityLogger !== undefined) { userActivityLogger(`User updated restore '${backupId}'`, { - project: '', - event: 'api:updateRestore', - payload: { - backupId, - patch, - backupData, - ...auditLog, - } - }); + project: '', + event: 'api:updateRestore', + payload: { + backupId, + patch, + backupData, + ...auditLog, + }, + }); } return restoreData; }; - export const deleteRestore: ResolverFn = async ( root, { input: { backupId, - } + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } ) => { - let rows = await query(sqlClientPool, Sql.selectBackupByBackupId(backupId)); - if (rows.length == 0) { + if (rows.length === 0) { throw new Error( - `Unauthorized: You don't have permission to "delete" on "restore"` + 'Unauthorized: You don\'t have permission to "delete" on "restore"', ); } const backupData = R.prop(0, rows); const permsRestore = await query( sqlClientPool, - Sql.selectPermsForRestore(backupId) + Sql.selectPermsForRestore(backupId), ); // Check access to modify restore as it currently stands await hasPermission('restore', 'delete', { - project: R.path(['0', 'pid'], permsRestore) + project: R.path(['0', 'pid'], permsRestore), }); rows = await query(sqlClientPool, Sql.selectRestoreByBackupId(backupId)); - if (rows.length == 0) { + if (rows.length === 0) { throw new Error( - `No restore file found for backup` + 'No restore file found for backup', ); } const restoreData = R.prop(0, rows); await deleteRestoreLocation(backupId, restoreData.restoreLocation, sqlClientPool); - const environmentData = await query(sqlClientPool, environmentSql.selectEnvironmentBySnapshotId(backupId)) + const environmentData = await query(sqlClientPool, environmentSql.selectEnvironmentBySnapshotId(backupId)); const project = await projectHelpers(sqlClientPool).getProjectById(environmentData[0].project); pubSub.publish(EVENTS.BACKUP, backupData); @@ -612,7 +617,7 @@ export const deleteRestore: ResolverFn = async ( if (project.organization) { auditLog.organizationId = project.organization; } - if (userActivityLogger != undefined) { + if (userActivityLogger !== undefined) { userActivityLogger(`User deleted restored file for backup '${backupId}'`, { project: '', event: 'api:deleteRestore', @@ -620,7 +625,7 @@ export const deleteRestore: ResolverFn = async ( backupId, backupData, ...auditLog, - } + }, }); } @@ -630,56 +635,57 @@ export const deleteRestore: ResolverFn = async ( export const getBackupDownloadLinkByBackupId: ResolverFn = async ( root, { backupId }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, + }, ) => { const perms = await query(sqlClientPool, Sql.selectPermsForBackup(backupId)); await hasPermission('backup', 'view', { - project: R.path(['0', 'pid'], perms) + project: R.path(['0', 'pid'], perms), }); const rows = await query( sqlClientPool, - Sql.selectRestoreByBackupId(backupId) + Sql.selectRestoreByBackupId(backupId), ); - const row = R.prop(0, rows) + const row = R.prop(0, rows); if (!row || row.restoreLocation == null) { - throw new Error(`no restore file available`); + throw new Error('no restore file available'); } const backupData = R.prop(0, rows); const restoreLoc = await getRestoreLocation(backupId, backupData.restoreLocation, sqlClientPool, userActivityLogger); return restoreLoc.restoreLocation; -} +}; export const getRestoreByBackupId: ResolverFn = async ( { backupId }, args, { sqlClientPool, userActivityLogger }, - info + info, ) => { const rows = await query( sqlClientPool, - Sql.selectRestoreByBackupId(backupId) + Sql.selectRestoreByBackupId(backupId), ); - const row = R.prop(0, rows) + const row = R.prop(0, rows); if (!row || row.restoreLocation == null) { return row; } - const restoreLocationRequested = info.fieldNodes[0].selectionSet.selections.find(item => item.name.value === "restoreLocation"); + const restoreLocationRequested = info.fieldNodes[0].selectionSet.selections.find(item => item.name.value === 'restoreLocation'); if (restoreLocationRequested) { // if the restore has a location, determine the signed url and the reported size of the object in Bytes const restoreLoc = await getRestoreLocation(backupId, row.restoreLocation, sqlClientPool, userActivityLogger); - return {...row, restoreLocation: restoreLoc.restoreLocation, restoreSize: restoreLoc.restoreSize}; - } else { - // if the restore does not have a location, return the row as is with restoreSize - const restoreLoc = await getRestoreLocation(backupId, row.restoreLocation, sqlClientPool, userActivityLogger, true); - return {...row, restoreSize: restoreLoc.restoreSize}; + return { ...row, restoreLocation: restoreLoc.restoreLocation, restoreSize: restoreLoc.restoreSize }; } + // if the restore does not have a location, return the row as is with restoreSize + const restoreLoc = await getRestoreLocation(backupId, row.restoreLocation, sqlClientPool, userActivityLogger, true); + return { ...row, restoreSize: restoreLoc.restoreSize }; }; export const backupSubscriber = createEnvironmentFilteredSubscriber([ - EVENTS.BACKUP + EVENTS.BACKUP, ]); diff --git a/services/api/src/resources/backup/sql.ts b/services/api/src/resources/backup/sql.ts index d28af324c2..42812ea1fe 100644 --- a/services/api/src/resources/backup/sql.ts +++ b/services/api/src/resources/backup/sql.ts @@ -1,145 +1,129 @@ import { knex } from '../../util/db'; export const Sql = { - selectBackup: id => - knex('environment_backup') - .where('id', '=', id) - .toString(), - selectBackupByBackupId: (backupId: number) => - knex('environment_backup') - .where('backup_id', '=', backupId) - .toString(), - selectEnvVariablesByProjectsById: (projectId: number) => - knex('env_vars') - .where('project', projectId) - .toString(), - selectBackupsByEnvironmentId: (environmentId: number) => - knex('environment_backup') - .where('environment', '=', environmentId) - .orderBy('created', 'desc') - .orderBy('id', 'desc') - .toString(), + selectBackup: id => knex('environment_backup') + .where('id', '=', id) + .toString(), + selectBackupByBackupId: (backupId: number) => knex('environment_backup') + .where('backup_id', '=', backupId) + .toString(), + selectEnvVariablesByProjectsById: (projectId: number) => knex('env_vars') + .where('project', projectId) + .toString(), + selectBackupsByEnvironmentId: (environmentId: number) => knex('environment_backup') + .where('environment', '=', environmentId) + .orderBy('created', 'desc') + .orderBy('id', 'desc') + .toString(), insertBackup: ({ id, environment, source, backupId, - created + created, }: { id: number; environment: number; source: string; backupId: string; created: string; - }) => - knex('environment_backup') - .insert({ - id, - environment, - source, - backup_id: backupId, - created - }) - .toString(), - deleteBackup: (backupId: string) => - knex('environment_backup') - .where('backup_id', backupId) - .delete() // actually delete the backup, there is no real reason to retain this information, the snapshot is gone - .toString(), - truncateBackup: () => - knex('environment_backup') - .truncate() - .toString(), - selectRestore: (id: number) => - knex('backup_restore') - .where('id', '=', id) - .toString(), - selectRestoreByBackupId: (backupId: string) => - knex('backup_restore') - .where('backup_id', backupId) - .toString(), + }) => knex('environment_backup') + .insert({ + id, + environment, + source, + backup_id: backupId, + created, + }) + .toString(), + deleteBackup: (backupId: string) => knex('environment_backup') + .where('backup_id', backupId) + .delete() // actually delete the backup, there is no real reason to retain this information, the snapshot is gone + .toString(), + truncateBackup: () => knex('environment_backup') + .truncate() + .toString(), + selectRestore: (id: number) => knex('backup_restore') + .where('id', '=', id) + .toString(), + selectRestoreByBackupId: (backupId: string) => knex('backup_restore') + .where('backup_id', backupId) + .toString(), insertRestore: ({ id, backupId, status, restoreLocation, - created + created, }: { id: number; backupId: string; status: string; restoreLocation: string; created: string; - }) => - knex('backup_restore') - .insert({ - id, - backupId, - status, - restoreLocation, - created - }) - .toString(), + }) => knex('backup_restore') + .insert({ + id, + backupId, + status, + restoreLocation, + created, + }) + .toString(), deleteRestore: ({ - backupId + backupId, }: { backupId: string; - }) => - knex('backup_restore') - .where({ - backupId: backupId - }) - .delete() - .toString(), + }) => knex('backup_restore') + .where({ + backupId, + }) + .delete() + .toString(), updateRestore: ({ backupId, - patch + patch, }: { backupId: string; patch: { [key: string]: any }; - }) => - knex('backup_restore') - .where('backup_id', backupId) - .update(patch) - .toString(), - selectPermsForRestore: (backupId: string) => - knex('backup_restore') - .select({ pid: 'environment.project' }) - .join( - 'environment_backup', - 'backup_restore.backup_id', - '=', - 'environment_backup.id' - ) - .join( - 'environment', - 'environment_backup.environment', - '=', - 'environment.id' - ) - .where('backup_restore.backup_id', backupId) - .toString(), - selectPermsForBackup: (backupId: string) => - knex('environment_backup') - .select({ pid: 'environment.project' }) - .join( - 'environment', - 'environment_backup.environment', - '=', - 'environment.id' - ) - .where('environment_backup.backup_id', backupId) - .toString(), + }) => knex('backup_restore') + .where('backup_id', backupId) + .update(patch) + .toString(), + selectPermsForRestore: (backupId: string) => knex('backup_restore') + .select({ pid: 'environment.project' }) + .join( + 'environment_backup', + 'backup_restore.backup_id', + '=', + 'environment_backup.id', + ) + .join( + 'environment', + 'environment_backup.environment', + '=', + 'environment.id', + ) + .where('backup_restore.backup_id', backupId) + .toString(), + selectPermsForBackup: (backupId: string) => knex('environment_backup') + .select({ pid: 'environment.project' }) + .join( + 'environment', + 'environment_backup.environment', + '=', + 'environment.id', + ) + .where('environment_backup.backup_id', backupId) + .toString(), // delete all environments backups from backup table that match environment id - deleteBackupsByEnvironmentId: (environmentId: number) => - knex('environment_backup') - .where('environment', '=', environmentId) - .delete() - .toString(), + deleteBackupsByEnvironmentId: (environmentId: number) => knex('environment_backup') + .where('environment', '=', environmentId) + .delete() + .toString(), // delete all environments backups from backup table that match environment ids - deleteBackupsByEnvironmentIds: (environmentIds: number[]) => - knex('environment_backup') - .whereIn('environment', environmentIds) - .delete() - .toString(), + deleteBackupsByEnvironmentIds: (environmentIds: number[]) => knex('environment_backup') + .whereIn('environment', environmentIds) + .delete() + .toString(), }; diff --git a/services/api/src/resources/deployment/helpers.ts b/services/api/src/resources/deployment/helpers.ts index 6bd9c2c549..cd210b9d62 100644 --- a/services/api/src/resources/deployment/helpers.ts +++ b/services/api/src/resources/deployment/helpers.ts @@ -1,6 +1,6 @@ import * as R from 'ramda'; -import { asyncPipe } from '@lagoon/commons/dist/util/func'; import { Pool } from 'mariadb'; +import { asyncPipe } from '../../commons/util/func'; import { query } from '../../util/db'; import { Sql } from './sql'; import { Helpers as environmentHelpers } from '../environment/helpers'; @@ -9,23 +9,23 @@ export const Helpers = (sqlClientPool: Pool) => { const getDeploymentById = async (deploymentID: number) => { const rows = await query( sqlClientPool, - Sql.selectDeployment(deploymentID) + Sql.selectDeployment(deploymentID), ); return R.prop(0, rows); }; // getSourceUser can decode the keycloak or legacy grant into a username or issuer name // this can then be stored against the deployment (or task) resource in the API when it is created - const getSourceUser =async (keycloakGrant, legacyGrant) => { - let sourceUser = "administrator" + const getSourceUser = async (keycloakGrant, legacyGrant) => { + let sourceUser = 'administrator'; if (keycloakGrant) { - sourceUser = keycloakGrant.access_token.content.email + sourceUser = keycloakGrant.access_token.content.email; } if (legacyGrant) { - sourceUser = legacyGrant.iss + sourceUser = legacyGrant.iss; } - return sourceUser - } + return sourceUser; + }; return { getSourceUser, @@ -36,7 +36,7 @@ export const Helpers = (sqlClientPool: Pool) => { const hasName = R.both(R.has('name'), R.propSatisfies(notEmpty, 'name')); const hasEnvironment = R.both( R.has('environment'), - R.propSatisfies(notEmpty, 'environment') + R.propSatisfies(notEmpty, 'environment'), ); // @ts-ignore const hasNameAndEnvironment = R.both(hasName, hasEnvironment); @@ -50,16 +50,16 @@ export const Helpers = (sqlClientPool: Pool) => { } return deployment; - } + }, ); const deploymentFromNameEnv = async input => { const environments = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentsByEnvironmentInput(R.prop('environment', input)); const activeEnvironments = R.filter( - R.propEq('deleted', '0000-00-00 00:00:00'), - environments + R.propEq('0000-00-00 00:00:00', 'deleted'), + environments, ); if (activeEnvironments.length < 1 || activeEnvironments.length > 1) { @@ -72,8 +72,8 @@ export const Helpers = (sqlClientPool: Pool) => { sqlClientPool, Sql.selectDeploymentByNameAndEnvironment( R.prop('name', input), - environment.id - ) + environment.id, + ), ); if (!R.prop(0, rows)) { @@ -91,12 +91,12 @@ export const Helpers = (sqlClientPool: Pool) => { R.T, () => { throw new Error( - 'Must provide deployment (id) or (name and environment)' + 'Must provide deployment (id) or (name and environment)', ); - } - ] + }, + ], // @ts-ignore ])(deploymentInput); - } + }, }; }; diff --git a/services/api/src/resources/deployment/resolvers.ts b/services/api/src/resources/deployment/resolvers.ts index a5d2ad078e..4642a47ef0 100644 --- a/services/api/src/resources/deployment/resolvers.ts +++ b/services/api/src/resources/deployment/resolvers.ts @@ -1,15 +1,17 @@ import * as R from 'ramda'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; +import sha1 from 'sha1'; +import uuid4 from 'uuid4'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; import { createMiscTask, sendToLagoonActions, - makeSafe -} from '@lagoon/commons/dist/tasks'; -import { ResolverFn } from '../'; + makeSafe, +} from '../../commons/tasks'; +import { ResolverFn } from '..'; import { pubSub, createEnvironmentFilteredSubscriber, - EVENTS + EVENTS, } from '../../clients/pubSub'; import { getConfigFromEnv, getLagoonRouteFromEnv } from '../../util/config'; import { knex, query, isPatchEmpty } from '../../util/db'; @@ -19,30 +21,32 @@ import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as retentionHelpers } from '../retentionpolicy/helpers'; import { HistoryRetentionEnforcer } from '../retentionpolicy/history'; import { Helpers as projectHelpers } from '../project/helpers'; -import { addTask } from '@lagoon/commons/dist/api'; +import { addTask } from '../../commons/api'; import { Sql as environmentSql } from '../environment/sql'; -const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); -import sha1 from 'sha1'; -import { generateBuildId, generateVariableOnlyBuildId } from '@lagoon/commons/dist/util/lagoon'; -import { encodeJSONBase64, jsonMerge } from '@lagoon/commons/dist/util/func'; + +import { generateBuildId, generateVariableOnlyBuildId } from '../../commons/util/lagoon'; +import { encodeJSONBase64, jsonMerge } from '../../commons/util/func'; import { logger } from '../../loggers/logger'; import { getUserProjectIdsFromRoleProjectIds } from '../../util/auth'; -import uuid4 from 'uuid4'; -import { DeploymentSourceType, DeployType, TaskStatusType, TaskSourceType, DeployData, AuditType, DeployPullrequest, DeploymentBuildType } from '@lagoon/commons/dist/types'; +import { + DeploymentSourceType, DeployType, TaskStatusType, TaskSourceType, DeployData, AuditType, DeployPullrequest, DeploymentBuildType, +} from '../../commons/types'; import { AuditLog } from '../audit/types'; -const accessKeyId = process.env.S3_FILES_ACCESS_KEY_ID || 'minio' -const secretAccessKey = process.env.S3_FILES_SECRET_ACCESS_KEY || 'minio123' -const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files' -const region = process.env.S3_FILES_REGION || 'us-east-1' -const s3Origin = process.env.S3_FILES_HOST || 'http://docker.for.mac.localhost:9000' +const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); + +const accessKeyId = process.env.S3_FILES_ACCESS_KEY_ID || 'minio'; +const secretAccessKey = process.env.S3_FILES_SECRET_ACCESS_KEY || 'minio123'; +const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files'; +const region = process.env.S3_FILES_REGION || 'us-east-1'; +const s3Origin = process.env.S3_FILES_HOST || 'http://docker.for.mac.localhost:9000'; const config = { origin: s3Origin, - accessKeyId: accessKeyId, - secretAccessKey: secretAccessKey, - region: region, - bucket: bucket + accessKeyId, + secretAccessKey, + region, + bucket, }; const s3Client = new S3Client({ @@ -53,40 +57,42 @@ const s3Client = new S3Client({ }, region: config.region, forcePathStyle: true, - signatureVersion: 'v4' + signatureVersion: 'v4', }); const convertDateFormat = R.init; export const getBuildLog: ResolverFn = async ( - { remoteId, environment, name, status }, + { + remoteId, environment, name, _status, + }, _args, - { sqlClientPool } + { sqlClientPool }, ) => { if (!remoteId) { return null; } const environmentData = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(parseInt(environment)); const projectData = await projectHelpers(sqlClientPool).getProjectById( - environmentData.project + environmentData.project, ); // we need to get the safename of the environment from when it was created - const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g,'-') - var environmentName = makeSafe(environmentData.name) - var overlength = 58 - projectData.name.length; - if ( environmentName.length > overlength ) { - var hash = sha1(environmentName).substring(0,4) - environmentName = environmentName.substring(0, overlength-5) - environmentName = environmentName.concat('-' + hash) + const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g, '-'); + let environmentName = makeSafe(environmentData.name); + const overlength = 58 - projectData.name.length; + if (environmentName.length > overlength) { + const hash = sha1(environmentName).substring(0, 4); + environmentName = environmentName.substring(0, overlength - 5); + environmentName = environmentName.concat(`-${hash}`); } try { // where it should be, check `buildlogs/projectName/environmentName/buildName-remoteId.txt` - let buildLog = 'buildlogs/'+projectData.name+'/'+environmentName+'/'+name+'-'+remoteId+'.txt' + const buildLog = `buildlogs/${projectData.name}/${environmentName}/${name}-${remoteId}.txt`; const response = await s3Client.send(new GetObjectCommand({ Bucket: bucket, Key: buildLog })); const data = await response.Body.transformToString('utf-8'); @@ -104,9 +110,10 @@ export const getBuildLog: ResolverFn = async ( export const getDeploymentsByBulkId: ResolverFn = async ( root, { bulkId }, - { sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups } + { + sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups, + }, ) => { - /* use the same mechanism for viewing all projects bulk deployments can span multiple projects, and anyone with access to a project @@ -118,7 +125,7 @@ export const getDeploymentsByBulkId: ResolverFn = async ( let userProjectIds: number[]; try { await hasPermission('project', 'viewAll'); - } catch (err) { + } catch (_err) { if (!keycloakGrant) { logger.debug('No grant available for getDeploymentsByBulkId'); return []; @@ -130,16 +137,16 @@ export const getDeploymentsByBulkId: ResolverFn = async ( let queryBuilder = knex('deployment') .where('deployment.bulk_id', bulkId) - .toString() + .toString(); if (userProjectIds) { queryBuilder = knex('environment') - .select('deployment.*') - .join('deployment', 'environment.id', '=', 'deployment.environment') - .join('project', 'environment.project', '=', 'project.id') - .whereIn('project.id', userProjectIds) - .andWhere('deployment.bulk_id', bulkId) - .toString() + .select('deployment.*') + .join('deployment', 'environment.id', '=', 'deployment.environment') + .join('project', 'environment.project', '=', 'project.id') + .whereIn('project.id', userProjectIds) + .andWhere('deployment.bulk_id', bulkId) + .toString(); } const rows = await query(sqlClientPool, queryBuilder.toString()); @@ -150,19 +157,20 @@ export const getDeploymentsByBulkId: ResolverFn = async ( export const getDeploymentsByFilter: ResolverFn = async ( root, input, - { sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups, adminScopes } + { + sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups, + }, ) => { - - let { + const { openshifts, - deploymentStatus = ["NEW", "PENDING", "RUNNING", "QUEUED"], + deploymentStatus = ['NEW', 'PENDING', 'RUNNING', 'QUEUED'], environmentType, startDate, endDate, includeDeleted, - limitPerEnvironment, - limit + limit, } = input; + let { limitPerEnvironment } = input; /* use the same mechanism for viewing all projects @@ -175,7 +183,7 @@ export const getDeploymentsByFilter: ResolverFn = async ( let userProjectIds: number[]; try { await hasPermission('project', 'viewAll'); - } catch (err) { + } catch (_err) { if (!keycloakGrant) { logger.debug('No grant available for getDeploymentsByFilter'); return []; @@ -191,13 +199,13 @@ export const getDeploymentsByFilter: ResolverFn = async ( limitPerEnvironment = 1; } - let limitedStatuses = ["complete", "failed", "cancelled"]; + const limitedStatuses = ['complete', 'failed', 'cancelled']; if (!limitPerEnvironment && deploymentStatus.some(element => limitedStatuses.includes(element))) { // if a user requests `complete`, `failed`, or `cancelled` status builds but does not include a `limitPerEnvironment` limit // set the limit to the last 5 deployments to limit the amount of returned data limitPerEnvironment = 5; } - logger.info( Sql.selectDeploymentsByFilter( + logger.info(Sql.selectDeploymentsByFilter( { startDate, endDate, @@ -207,8 +215,8 @@ export const getDeploymentsByFilter: ResolverFn = async ( deploymentStatus, includeDeleted, limitPerEnvironment, - limit - } + limit, + }, )); const rows = await query(sqlClientPool, Sql.selectDeploymentsByFilter( { @@ -220,8 +228,8 @@ export const getDeploymentsByFilter: ResolverFn = async ( deploymentStatus, includeDeleted, limitPerEnvironment, - limit - } + limit, + }, )); const withK8s = projectHelpers(sqlClientPool).aliasOpenshiftToK8s(rows); @@ -231,23 +239,23 @@ export const getDeploymentsByFilter: ResolverFn = async ( export const getDeploymentsByEnvironmentId: ResolverFn = async ( { id: eid }, { name, limit }, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(eid); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('deployment', 'view', { - project: environment.project + project: environment.project, }); } let queryBuilder = knex('deployment') - .where('environment', eid) - .orderBy('created', 'desc') - .orderBy('id', 'desc'); + .where('environment', eid) + .orderBy('created', 'desc') + .orderBy('id', 'desc'); if (name) { queryBuilder = queryBuilder.andWhere('name', name); @@ -263,7 +271,7 @@ export const getDeploymentsByEnvironmentId: ResolverFn = async ( export const getDeploymentByRemoteId: ResolverFn = async ( _root, { id }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const queryString = knex('deployment') .where('remote_id', '=', id) @@ -278,11 +286,11 @@ export const getDeploymentByRemoteId: ResolverFn = async ( const perms = await query( sqlClientPool, - Sql.selectPermsForDeployment(deployment.id) + Sql.selectPermsForDeployment(deployment.id), ); await hasPermission('deployment', 'view', { - project: R.path(['0', 'pid'], perms) + project: R.path(['0', 'pid'], perms), }); return deployment; @@ -291,19 +299,18 @@ export const getDeploymentByRemoteId: ResolverFn = async ( export const getDeploymentByName: ResolverFn = async ( _root, { input: { project: projectName, environment: environmentName, name } }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { - const projectId = await projectHelpers(sqlClientPool).getProjectIdByName( - projectName + projectName, ); await hasPermission('deployment', 'view', { - project: projectId + project: projectId, }); const environmentRows = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject( - environmentName, projectId + environmentName, projectId, ); const queryString = knex('deployment') @@ -324,15 +331,15 @@ export const getDeploymentByName: ResolverFn = async ( export const getDeploymentUrl: ResolverFn = async ( { id, environment }, _args, - { sqlClientPool } + { sqlClientPool }, ) => { const lagoonUiRoute = getLagoonRouteFromEnv( /\/ui-/, - getConfigFromEnv('UI_URL', 'http://localhost:8888') + getConfigFromEnv('UI_URL', 'http://localhost:8888'), ); const { name: project, openshiftProjectName } = await projectHelpers( - sqlClientPool + sqlClientPool, ).getProjectByEnvironmentId(environment); const deployment = await Helpers(sqlClientPool).getDeploymentById(id); @@ -359,22 +366,24 @@ export const addDeployment: ResolverFn = async ( sourceUser, sourceType, buildType, - } + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant } ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); await hasPermission('environment', `deploy:${environment.environmentType}`, { - project: environment.project + project: environment.project, }); - sourceUser ??= await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) - sourceType ??= DeploymentSourceType.API + sourceUser ??= await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); + sourceType ??= DeploymentSourceType.API; const projectData = await projectHelpers(sqlClientPool).getProjectById( - environment.project + environment.project, ); const { insertId } = await query( @@ -395,7 +404,7 @@ export const addDeployment: ResolverFn = async ( sourceType, sourceUser, buildType, - }) + }), ); const deployment = await Helpers(sqlClientPool).getDeploymentById(insertId); @@ -420,11 +429,11 @@ export const addDeployment: ResolverFn = async ( payload: { deployment: deployment.id, ...auditLog, - } + }, }); // pass to the HistoryRetentionEnforcer to clean up deployments based on any retention policies - await HistoryRetentionEnforcer().cleanupDeployments(projectData, environment) + await HistoryRetentionEnforcer().cleanupDeployments(projectData, environment); pubSub.publish(EVENTS.DEPLOYMENT, deployment); return deployment; @@ -433,25 +442,24 @@ export const addDeployment: ResolverFn = async ( export const deleteDeployment: ResolverFn = async ( root, { input: { id } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const perms = await query(sqlClientPool, Sql.selectPermsForDeployment(id)); await hasPermission('deployment', 'delete', { - project: R.path(['0', 'pid'], perms) + project: R.path(['0', 'pid'], perms), }); - - const deployment = await Helpers(sqlClientPool).getDeploymentById(id) + const deployment = await Helpers(sqlClientPool).getDeploymentById(id); if (!deployment) { throw new Error( - `Invalid deployment input` + 'Invalid deployment input', ); } const environment = await environmentHelpers(sqlClientPool).getEnvironmentById( - deployment.environment + deployment.environment, ); await query(sqlClientPool, Sql.deleteDeployment(id)); @@ -477,15 +485,15 @@ export const deleteDeployment: ResolverFn = async ( await query(sqlClientPool, Sql.deleteDeployment(id)); // pass the deployment to the HistoryRetentionEnforcer - await HistoryRetentionEnforcer().cleanupDeployment(projectData, environmentData, deployment) + await HistoryRetentionEnforcer().cleanupDeployment(projectData, environmentData, deployment); userActivityLogger(`User deleted deployment '${id}'`, { project: '', event: 'api:deleteDeployment', payload: { deployment: id, - ...auditLog - } + ...auditLog, + }, }); return 'success'; @@ -508,11 +516,11 @@ export const updateDeployment: ResolverFn = async ( priority, bulkId, bulkName, - buildStep - } - } + buildStep, + }, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { if (isPatchEmpty({ patch })) { throw new Error('Input patch requires at least 1 attribute'); @@ -520,13 +528,13 @@ export const updateDeployment: ResolverFn = async ( const permsDeployment = await query( sqlClientPool, - Sql.selectPermsForDeployment(id) + Sql.selectPermsForDeployment(id), ); - const projectId = R.path(['0', 'pid'], permsDeployment) + const projectId = R.path(['0', 'pid'], permsDeployment); // Check access to modify deployment as it currently stands await hasPermission('deployment', 'update', { - project: projectId + project: projectId, }); await query( @@ -544,9 +552,9 @@ export const updateDeployment: ResolverFn = async ( priority, bulkId, bulkName, - buildStep - } - }) + buildStep, + }, + }), ); const deployment = await Helpers(sqlClientPool).getDeploymentById(id); @@ -554,15 +562,15 @@ export const updateDeployment: ResolverFn = async ( pubSub.publish(EVENTS.DEPLOYMENT, deployment); const env = await environmentHelpers(sqlClientPool).getEnvironmentById( - deployment.environment + deployment.environment, ); const project = await projectHelpers(sqlClientPool).getProjectById(env.project); try { // handle retention policy hooks - await retentionHelpers(sqlClientPool).postDeploymentProjectPolicyHook(parseInt(projectId.toString(), 10), status) + await retentionHelpers(sqlClientPool).postDeploymentProjectPolicyHook(parseInt(projectId.toString(), 10), status); } catch (e) { - logger.warn(`failed to perform postDeploymentProjectPolicyHook: ${e}`) + logger.warn(`failed to perform postDeploymentProjectPolicyHook: ${e}`); } const auditLog: AuditLog = { @@ -597,10 +605,10 @@ export const updateDeployment: ResolverFn = async ( priority, bulkId, bulkName, - buildStep + buildStep, }, ...auditLog, - } + }, }); return deployment; @@ -609,26 +617,26 @@ export const updateDeployment: ResolverFn = async ( export const cancelDeployment: ResolverFn = async ( root, { input: { deployment: deploymentInput } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const deployment = await Helpers( - sqlClientPool + sqlClientPool, ).getDeploymentByDeploymentInput(deploymentInput); const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(deployment.environment); const project = await projectHelpers(sqlClientPool).getProjectById( - environment.project + environment.project, ); await hasPermission('deployment', 'cancel', { - project: project.id + project: project.id, }); const data = { build: deployment, environment, - project + project, }; const auditLog: AuditLog = { @@ -653,35 +661,35 @@ export const cancelDeployment: ResolverFn = async ( deploymentInput, data: data.build, ...auditLog, - } + }, }); // check if the deploytarget for this environment is disabled const deploytarget = await environmentHelpers(sqlClientPool).getEnvironmentsDeploytarget(environment.openshift); if (deploytarget[0].disabled) { // if it is, proceed to mark the build as cancelled - var date = new Date(); - var completed = convertDateFormat(date.toISOString()); + const date = new Date(); + const completed = convertDateFormat(date.toISOString()); await query( sqlClientPool, Sql.updateDeployment({ id: deployment.id, patch: { - status: "cancelled", + status: 'cancelled', completed, environment: environment.id, - } - }) + }, + }), ); // just normal lagoon environment name conversion to lowercase and safeing - const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g,'-') - var environmentName = makeSafe(environment.name) - var overlength = 58 - project.name.length; - if ( environmentName.length > overlength ) { - var hash = sha1(environmentName).substring(0,4) - environmentName = environmentName.substring(0, overlength-5) - environmentName = environmentName.concat('-' + hash) + const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g, '-'); + let environmentName = makeSafe(environment.name); + const overlength = 58 - project.name.length; + if (environmentName.length > overlength) { + const hash = sha1(environmentName).substring(0, 4); + environmentName = environmentName.substring(0, overlength - 5); + environmentName = environmentName.concat(`-${hash}`); } // then publish a message to the logs system with the build log sendToLagoonLogs( @@ -689,16 +697,17 @@ export const cancelDeployment: ResolverFn = async ( project.name, '', `build-logs:builddeploy-kubernetes:${deployment.name}`, - { branchName: environmentName, + { + branchName: environmentName, buildName: deployment.name, - buildStatus: "cancelled", - buildPhase: "cancelled", - buildStep: "cancelled", + buildStatus: 'cancelled', + buildPhase: 'cancelled', + buildStep: 'cancelled', environmentId: environment.id, projectId: project.id, remoteId: deployment.remoteId, - }, - `========================================\nCancelled build as deploytarget '${deploytarget[0].name}' is disabled\n========================================\n` + }, + `========================================\nCancelled build as deploytarget '${deploytarget[0].name}' is disabled\n========================================\n`, ); return 'success'; } @@ -713,7 +722,7 @@ export const cancelDeployment: ResolverFn = async ( '', 'api:cancelDeployment', { deploymentId: deployment.id }, - `Deployment not cancelled, reason: ${error}` + `Deployment not cancelled, reason: ${error}`, ); return `Error: ${error.message}`; } @@ -721,21 +730,23 @@ export const cancelDeployment: ResolverFn = async ( export const deployEnvironmentLatest: ResolverFn = async ( root, - { input: { - environment: environmentInput, - priority, - bulkId, - bulkName, - buildVariables, - returnData - } + { + input: { + environment: environmentInput, + priority, + bulkId, + bulkName, + buildVariables, + returnData, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant } ) => { - try { await hasPermission('project', 'viewAll'); - } catch (err) { + } catch (_err) { // if the user isn't an admin, then they aren't allowed to set these values on deployments // the actions-handler service is an admin, so it has permission to do so if (bulkId) { @@ -751,11 +762,11 @@ export const deployEnvironmentLatest: ResolverFn = async ( } const environments = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentsByEnvironmentInput(environmentInput); const activeEnvironments = R.filter( - R.propEq('deleted', '0000-00-00 00:00:00'), - environments + R.propEq('0000-00-00 00:00:00', 'deleted'), + environments, ); if (activeEnvironments.length < 1 || activeEnvironments.length > 1) { @@ -764,128 +775,128 @@ export const deployEnvironmentLatest: ResolverFn = async ( const environment = R.prop(0, activeEnvironments); const project = await projectHelpers(sqlClientPool).getProjectById( - environment.project + environment.project, ); await hasPermission('environment', `deploy:${environment.environmentType}`, { - project: project.id + project: project.id, }); - if (project.deploymentsDisabled == 1){ + if (project.deploymentsDisabled === 1) { throw new Error('Deployments have been disabled for this project'); } if ( - environment.deployType === DeployType.BRANCH || - environment.deployType === DeployType.PROMOTE + environment.deployType === DeployType.BRANCH + || environment.deployType === DeployType.PROMOTE ) { if (!environment.deployBaseRef) { throw new Error('Cannot deploy: deployBaseRef is empty'); } } else if (environment.deployType === DeployType.PULLREQUEST) { if ( - !environment.deployBaseRef && - !environment.deployHeadRef && - !environment.deployTitle + !environment.deployBaseRef + && !environment.deployHeadRef + && !environment.deployTitle ) { throw new Error( - 'Cannot deploy: deployBaseRef, deployHeadRef or deployTitle is empty' + 'Cannot deploy: deployBaseRef, deployHeadRef or deployTitle is empty', ); } } let buildName = generateBuildId(); - let buildType = DeploymentBuildType.BUILD + let buildType = DeploymentBuildType.BUILD; // change the buildname to a variables only name if the build variable for lagoon variables only is found - if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === "true")) { + if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === 'true')) { buildName = generateVariableOnlyBuildId(); - buildType = DeploymentBuildType.VARIABLES + buildType = DeploymentBuildType.VARIABLES; } - const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) - let meta: { + const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); + const meta: { [key: string]: any; } = { - projectName: project.name + projectName: project.name, }; let deployData: DeployData; switch (environment.deployType) { case DeployType.BRANCH: deployData = { type: environment.deployType, - buildName: buildName, + buildName, branchName: environment.deployBaseRef, - sourceUser: sourceUser, + sourceUser, projectName: project.name, - buildType: buildType, + buildType, }; if (priority) { - deployData.buildPriority = priority + deployData.buildPriority = priority; } if (buildVariables) { - deployData.buildVariables = encodeJSONBase64(buildVariables) + deployData.buildVariables = encodeJSONBase64(buildVariables); } if (bulkId) { - deployData.bulkId = bulkId + deployData.bulkId = bulkId; } if (bulkName) { - deployData.bulkName = bulkName + deployData.bulkName = bulkName; } break; case DeployType.PULLREQUEST: deployData = { type: environment.deployType, - buildName: buildName, + buildName, branchName: environment.name, - sourceUser: sourceUser, + sourceUser, projectName: project.name, - buildType: buildType, - } + buildType, + }; if (priority) { - deployData.buildPriority = priority + deployData.buildPriority = priority; } - let pullrequest: DeployPullrequest = { + const pullrequest: DeployPullrequest = { title: environment.deployTitle, number: environment.name.replace('pr-', ''), headBranch: environment.deployHeadRef, headSha: `origin/${environment.deployHeadRef}`, baseBranch: environment.deployBaseRef, - baseSha: `origin/${environment.deployBaseRef}` - } - deployData.pullrequest = encodeJSONBase64(pullrequest) + baseSha: `origin/${environment.deployBaseRef}`, + }; + deployData.pullrequest = encodeJSONBase64(pullrequest); if (buildVariables) { - deployData.buildVariables = encodeJSONBase64(buildVariables) + deployData.buildVariables = encodeJSONBase64(buildVariables); } if (bulkId) { - deployData.bulkId = bulkId + deployData.bulkId = bulkId; } if (bulkName) { - deployData.bulkName = bulkName + deployData.bulkName = bulkName; } break; case DeployType.PROMOTE: deployData = { type: environment.deployType, - buildName: buildName, + buildName, branchName: environment.name, - sourceUser: sourceUser, + sourceUser, projectName: project.name, promoteSourceEnvironment: environment.deployBaseRef, - buildType: buildType, - } + buildType, + }; if (priority) { - deployData.buildPriority = priority + deployData.buildPriority = priority; } if (buildVariables) { - deployData.buildVariables = encodeJSONBase64(buildVariables) + deployData.buildVariables = encodeJSONBase64(buildVariables); } if (bulkId) { - deployData.bulkId = bulkId + deployData.bulkId = bulkId; } if (bulkName) { - deployData.bulkName = bulkName + deployData.bulkName = bulkName; } break; @@ -914,7 +925,7 @@ export const deployEnvironmentLatest: ResolverFn = async ( payload: { deployData, ...auditLog, - } + }, }); try { @@ -931,7 +942,7 @@ export const deployEnvironmentLatest: ResolverFn = async ( ); if (!response.ok) { const errorText = await response.text(); - logger.error(`Error deploying ${environment.name}: ${errorText}`) + logger.error(`Error deploying ${environment.name}: ${errorText}`); throw new Error(`Error deploying ${environment.name}`); } @@ -941,10 +952,10 @@ export const deployEnvironmentLatest: ResolverFn = async ( '', 'api:deployEnvironmentLatest', meta, - `*[${deployData.projectName}]* Deployment triggered \`${environment.name}\`` + `*[${deployData.projectName}]* Deployment triggered \`${environment.name}\``, ); - if (returnData == true) { + if (returnData === true) { return deployData.buildName; } return 'success'; @@ -957,7 +968,7 @@ export const deployEnvironmentLatest: ResolverFn = async ( '', 'api:deployEnvironmentLatest', meta, - `*[${deployData.projectName}]* Deployment skipped \`${environment.name}\`: ${error.message}` + `*[${deployData.projectName}]* Deployment skipped \`${environment.name}\`: ${error.message}`, ); return `Skipped: ${error.message}`; @@ -968,7 +979,7 @@ export const deployEnvironmentLatest: ResolverFn = async ( '', 'api:deployEnvironmentLatest:error', meta, - `*[${deployData.projectName}]* Error deploying \`${environment.name}\`: ${error.message}` + `*[${deployData.projectName}]* Error deploying \`${environment.name}\`: ${error.message}`, ); throw new Error(error.message); } @@ -977,68 +988,70 @@ export const deployEnvironmentLatest: ResolverFn = async ( export const deployEnvironmentBranch: ResolverFn = async ( root, - { input: { - project: projectInput, - branchName, - branchRef, - priority, - bulkId, - bulkName, - buildVariables, - returnData - } + { + input: { + project: projectInput, + branchName, + branchRef, + priority, + bulkId, + bulkName, + buildVariables, + returnData, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant } ) => { const project = await projectHelpers(sqlClientPool).getProjectByProjectInput( - projectInput + projectInput, ); - const envType = - branchName === project.productionEnvironment ? 'production' : 'development'; + const envType = branchName === project.productionEnvironment ? 'production' : 'development'; await hasPermission('environment', `deploy:${envType}`, { - project: project.id + project: project.id, }); - if (project.deploymentsDisabled == 1){ + if (project.deploymentsDisabled === 1) { throw new Error('Deployments have been disabled for this project'); } let buildName = generateBuildId(); - let buildType = DeploymentBuildType.BUILD + let buildType = DeploymentBuildType.BUILD; // change the buildname to a variables only name if the build variable for lagoon variables only is found - if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === "true")) { + if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === 'true')) { buildName = generateVariableOnlyBuildId(); - buildType = DeploymentBuildType.VARIABLES + buildType = DeploymentBuildType.VARIABLES; } - const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const deployData: DeployData = { type: DeployType.BRANCH, - buildName: buildName, - branchName: branchName, - sourceUser: sourceUser, + buildName, + branchName, + sourceUser, projectName: project.name, - buildType: buildType, + buildType, }; if (priority) { - deployData.buildPriority = priority + deployData.buildPriority = priority; } if (branchRef) { - deployData.gitSha = branchRef + deployData.gitSha = branchRef; } if (buildVariables) { - deployData.buildVariables = encodeJSONBase64(buildVariables) + deployData.buildVariables = encodeJSONBase64(buildVariables); } if (bulkId) { - deployData.bulkId = bulkId + deployData.bulkId = bulkId; } if (bulkName) { - deployData.bulkName = bulkName + deployData.bulkName = bulkName; } const meta = { projectName: project.name, - branchName: deployData.branchName + branchName: deployData.branchName, }; const auditLog: AuditLog = { @@ -1061,7 +1074,7 @@ export const deployEnvironmentBranch: ResolverFn = async ( payload: { deployData, ...auditLog, - } + }, }); try { @@ -1078,7 +1091,7 @@ export const deployEnvironmentBranch: ResolverFn = async ( ); if (!response.ok) { const errorText = await response.text(); - logger.error(`Error deploying ${deployData.branchName}: ${errorText}`) + logger.error(`Error deploying ${deployData.branchName}: ${errorText}`); throw new Error(`Error deploying ${deployData.branchName}`); } @@ -1088,10 +1101,10 @@ export const deployEnvironmentBranch: ResolverFn = async ( '', 'api:deployEnvironmentBranch', meta, - `*[${deployData.projectName}]* Deployment triggered \`${deployData.branchName}\`` + `*[${deployData.projectName}]* Deployment triggered \`${deployData.branchName}\``, ); - if (returnData == true) { + if (returnData === true) { return deployData.buildName; } return 'success'; @@ -1104,7 +1117,7 @@ export const deployEnvironmentBranch: ResolverFn = async ( '', 'api:deployEnvironmentBranch', meta, - `*[${deployData.projectName}]* Deployment skipped \`${deployData.branchName}\`: ${error.message}` + `*[${deployData.projectName}]* Deployment skipped \`${deployData.branchName}\`: ${error.message}`, ); return `Skipped: ${error.message}`; @@ -1115,7 +1128,7 @@ export const deployEnvironmentBranch: ResolverFn = async ( '', 'api:deployEnvironmentBranch:error', meta, - `*[${deployData.projectName}]* Error deploying \`${deployData.branchName}\`: ${error}` + `*[${deployData.projectName}]* Error deploying \`${deployData.branchName}\`: ${error}`, ); throw new Error(error.message); } @@ -1137,66 +1150,67 @@ export const deployEnvironmentPullrequest: ResolverFn = async ( bulkId, bulkName, buildVariables, - returnData - } + returnData, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant } ) => { const branchName = `pr-${number}`; const project = await projectHelpers(sqlClientPool).getProjectByProjectInput( - projectInput + projectInput, ); - const envType = - branchName === project.productionEnvironment ? 'production' : 'development'; + const envType = branchName === project.productionEnvironment ? 'production' : 'development'; await hasPermission('environment', `deploy:${envType}`, { - project: project.id + project: project.id, }); - if (project.deploymentsDisabled == 1){ + if (project.deploymentsDisabled === 1) { throw new Error('Deployments have been disabled for this project'); } let buildName = generateBuildId(); - let buildType = DeploymentBuildType.BUILD + let buildType = DeploymentBuildType.BUILD; // change the buildname to a variables only name if the build variable for lagoon variables only is found - if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === "true")) { + if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === 'true')) { buildName = generateVariableOnlyBuildId(); - buildType = DeploymentBuildType.VARIABLES + buildType = DeploymentBuildType.VARIABLES; } - const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const deployData: DeployData = { type: DeployType.PULLREQUEST, - buildName: buildName, - branchName: branchName, - sourceUser: sourceUser, + buildName, + branchName, + sourceUser, projectName: project.name, - buildType: buildType, - } + buildType, + }; if (priority) { - deployData.buildPriority = priority + deployData.buildPriority = priority; } const pullrequest: DeployPullrequest = { - title: title, - number: number, + title, + number, headBranch: headBranchName, headSha: headBranchRef, baseBranch: baseBranchName, - baseSha: baseBranchRef - } - deployData.pullrequest = encodeJSONBase64(pullrequest) + baseSha: baseBranchRef, + }; + deployData.pullrequest = encodeJSONBase64(pullrequest); if (buildVariables) { - deployData.buildVariables = encodeJSONBase64(buildVariables) + deployData.buildVariables = encodeJSONBase64(buildVariables); } if (bulkId) { - deployData.bulkId = bulkId + deployData.bulkId = bulkId; } if (bulkName) { - deployData.bulkName = bulkName + deployData.bulkName = bulkName; } const meta = { projectName: project.name, - pullrequestTitle: pullrequest.title + pullrequestTitle: pullrequest.title, }; const auditLog: AuditLog = { @@ -1218,8 +1232,8 @@ export const deployEnvironmentPullrequest: ResolverFn = async ( event: 'api:deployEnvironmentPullrequest', payload: { deployData, - ...auditLog - } + ...auditLog, + }, }); try { @@ -1236,7 +1250,7 @@ export const deployEnvironmentPullrequest: ResolverFn = async ( ); if (!response.ok) { const errorText = await response.text(); - logger.error(`Error deploying ${deployData.branchName}: ${errorText}`) + logger.error(`Error deploying ${deployData.branchName}: ${errorText}`); throw new Error(`Error deploying ${deployData.branchName}`); } @@ -1246,10 +1260,10 @@ export const deployEnvironmentPullrequest: ResolverFn = async ( '', 'api:deployEnvironmentPullrequest', meta, - `*[${deployData.projectName}]* Deployment triggered \`${deployData.branchName}\`` + `*[${deployData.projectName}]* Deployment triggered \`${deployData.branchName}\``, ); - if (returnData == true) { + if (returnData === true) { return deployData.buildName; } return 'success'; @@ -1262,7 +1276,7 @@ export const deployEnvironmentPullrequest: ResolverFn = async ( '', 'api:deployEnvironmentPullrequest', meta, - `*[${deployData.projectName}]* Deployment skipped \`${deployData.branchName}\`: ${error.message}` + `*[${deployData.projectName}]* Deployment skipped \`${deployData.branchName}\`: ${error.message}`, ); return `Skipped: ${error.message}`; @@ -1273,7 +1287,7 @@ export const deployEnvironmentPullrequest: ResolverFn = async ( '', 'api:deployEnvironmentPullrequest:error', meta, - `*[${deployData.projectName}]* Error deploying \`${deployData.branchName}\`: ${error.message}` + `*[${deployData.projectName}]* Error deploying \`${deployData.branchName}\`: ${error.message}`, ); throw new Error(error.message); } @@ -1291,33 +1305,34 @@ export const deployEnvironmentPromote: ResolverFn = async ( bulkId, bulkName, buildVariables, - returnData - } + returnData, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant } ) => { const destProject = await projectHelpers( - sqlClientPool + sqlClientPool, ).getProjectByProjectInput(projectInput); - const envType = - destinationEnvironment === destProject.productionEnvironment - ? 'production' - : 'development'; + const envType = destinationEnvironment === destProject.productionEnvironment + ? 'production' + : 'development'; await hasPermission('environment', `deploy:${envType}`, { - project: destProject.id + project: destProject.id, }); - if (destProject.deploymentsDisabled == 1){ + if (destProject.deploymentsDisabled === 1) { throw new Error('Deployments have been disabled for this project'); } const sourceEnvironments = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentsByEnvironmentInput(sourceEnvironmentInput); const activeEnvironments = R.filter( - R.propEq('deleted', '0000-00-00 00:00:00'), - sourceEnvironments + R.propEq('0000-00-00 00:00:00', 'deleted'), + sourceEnvironments, ); if (activeEnvironments.length < 1 || activeEnvironments.length > 1) { @@ -1327,44 +1342,44 @@ export const deployEnvironmentPromote: ResolverFn = async ( const sourceEnvironment = R.prop(0, activeEnvironments); await hasPermission('environment', 'view', { - project: sourceEnvironment.project + project: sourceEnvironment.project, }); let buildName = generateBuildId(); - let buildType = DeploymentBuildType.BUILD + let buildType = DeploymentBuildType.BUILD; // change the buildname to a variables only name if the build variable for lagoon variables only is found - if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === "true")) { + if (buildVariables && buildVariables.find(e => e.name === 'LAGOON_VARIABLES_ONLY' && e.value === 'true')) { buildName = generateVariableOnlyBuildId(); - buildType = DeploymentBuildType.VARIABLES + buildType = DeploymentBuildType.VARIABLES; } - const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const deployData: DeployData = { type: DeployType.PROMOTE, - buildName: buildName, + buildName, branchName: destinationEnvironment, - sourceUser: sourceUser, + sourceUser, projectName: destProject.name, promoteSourceEnvironment: sourceEnvironment.name, - buildType: buildType, - } + buildType, + }; if (priority) { - deployData.buildPriority = priority + deployData.buildPriority = priority; } if (buildVariables) { - deployData.buildVariables = encodeJSONBase64(buildVariables) + deployData.buildVariables = encodeJSONBase64(buildVariables); } if (bulkId) { - deployData.bulkId = bulkId + deployData.bulkId = bulkId; } if (bulkName) { - deployData.bulkName = bulkName + deployData.bulkName = bulkName; } const meta = { projectName: deployData.projectName, branchName: deployData.branchName, - promoteSourceEnvironment: deployData.promoteSourceEnvironment + promoteSourceEnvironment: deployData.promoteSourceEnvironment, }; const auditLog: AuditLog = { @@ -1388,7 +1403,7 @@ export const deployEnvironmentPromote: ResolverFn = async ( payload: { deployData, ...auditLog, - } + }, }); try { @@ -1405,7 +1420,7 @@ export const deployEnvironmentPromote: ResolverFn = async ( ); if (!response.ok) { const errorText = await response.text(); - logger.error(`Error deploying ${deployData.branchName}: ${errorText}`) + logger.error(`Error deploying ${deployData.branchName}: ${errorText}`); throw new Error(`Error deploying ${deployData.branchName}`); } @@ -1415,10 +1430,10 @@ export const deployEnvironmentPromote: ResolverFn = async ( '', 'api:deployEnvironmentPromote', meta, - `*[${deployData.projectName}]* Deployment triggered \`${deployData.branchName}\`` + `*[${deployData.projectName}]* Deployment triggered \`${deployData.branchName}\``, ); - if (returnData == true) { + if (returnData === true) { return deployData.buildName; } return 'success'; @@ -1431,7 +1446,7 @@ export const deployEnvironmentPromote: ResolverFn = async ( '', 'api:deployEnvironmentPromote', meta, - `*[${deployData.projectName}]* Deployment skipped \`${deployData.branchName}\`: ${error.message}` + `*[${deployData.projectName}]* Deployment skipped \`${deployData.branchName}\`: ${error.message}`, ); return `Skipped: ${error.message}`; @@ -1442,7 +1457,7 @@ export const deployEnvironmentPromote: ResolverFn = async ( '', 'api:deployEnvironmentPromote:error', meta, - `*[${deployData.projectName}]* Error deploying \`${deployData.branchName}\`: ${error.message}` + `*[${deployData.projectName}]* Error deploying \`${deployData.branchName}\`: ${error.message}`, ); throw new Error(error.message); } @@ -1452,19 +1467,21 @@ export const deployEnvironmentPromote: ResolverFn = async ( export const switchActiveStandby: ResolverFn = async ( root, { input: { project: projectInput } }, - { sqlClientPool, hasPermission, keycloakGrant, legacyGrant, adminScopes, userActivityLogger } + { + sqlClientPool, hasPermission, keycloakGrant, legacyGrant, userActivityLogger, + }, ) => { const project = await projectHelpers(sqlClientPool).getProjectByProjectInput( - projectInput + projectInput, ); // active/standby really only should be between production environments - await hasPermission('environment', `deploy:production`, { - project: project.id + await hasPermission('environment', 'deploy:production', { + project: project.id, }); await hasPermission('task', 'view', { - project: project.id + project: project.id, }); if (project.standbyProductionEnvironment == null) { @@ -1474,14 +1491,14 @@ export const switchActiveStandby: ResolverFn = async ( '', 'api:switchActiveStandby', '', - `Failed to create active to standby task, reason: no standbyProductionEnvironment configured` + 'Failed to create active to standby task, reason: no standbyProductionEnvironment configured', ); - return `Error: no standbyProductionEnvironment configured`; + return 'Error: no standbyProductionEnvironment configured'; } - var environmentStandbyId - var environmentProdId - var environmentProd - var environmentStandby + let environmentStandbyId; + let environmentProdId; + let environmentProd; + let environmentStandby; // we want the task to show in the standby environment, as this is where the task will be initiated. try { // maybe the environments have slashes in their names @@ -1489,29 +1506,29 @@ export const switchActiveStandby: ResolverFn = async ( const environmentsForProject = await query( sqlClientPool, environmentSql.selectEnvironmentsByProjectID( - project.id, true - ) + project.id, true, + ), ); for (const envForProject of environmentsForProject) { // check the environments to see if their name when made "safe" matches what is defined in the `production` or `standbyProduction` environment - if (makeSafe(envForProject.name) == project.productionEnvironment) { + if (makeSafe(envForProject.name) === project.productionEnvironment) { const environmentRowsProd = await query( sqlClientPool, environmentSql.selectEnvironmentByNameAndProject( envForProject.name, - project.id - ) + project.id, + ), ); environmentProd = environmentRowsProd[0]; environmentProdId = parseInt(environmentProd.id); } - if (makeSafe(envForProject.name) == project.standbyProductionEnvironment) { + if (makeSafe(envForProject.name) === project.standbyProductionEnvironment) { const environmentRows = await query( sqlClientPool, environmentSql.selectEnvironmentByNameAndProject( envForProject.name, - project.id - ) + project.id, + ), ); environmentStandby = environmentRows[0]; environmentStandbyId = parseInt(environmentStandby.id); @@ -1522,7 +1539,7 @@ export const switchActiveStandby: ResolverFn = async ( } if (environmentStandbyId === undefined || environmentProdId === undefined) { - throw new Error(`Unable to determine active standby environments`); + throw new Error('Unable to determine active standby environments'); } // construct the data for the misc task // set up the task data payload @@ -1531,30 +1548,30 @@ export const switchActiveStandby: ResolverFn = async ( id: project.id, name: project.name, productionEnvironment: project.productionEnvironment, - standbyProductionEnvironment: project.standbyProductionEnvironment + standbyProductionEnvironment: project.standbyProductionEnvironment, }, productionEnvironment: { id: environmentProdId, name: environmentProd.name, - openshiftProjectName: environmentProd.openshiftProjectName + openshiftProjectName: environmentProd.openshiftProjectName, }, environment: { id: environmentStandbyId, name: environmentStandby.name, - openshiftProjectName: environmentStandby.openshiftProjectName + openshiftProjectName: environmentStandby.openshiftProjectName, }, task: { id: '0', - name: 'Active/Standby Switch' - } + name: 'Active/Standby Switch', + }, }; // try it now - const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await Helpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); try { // add a task into the environment - var date = new Date(); - var created = convertDateFormat(date.toISOString()); + const date = new Date(); + const created = convertDateFormat(date.toISOString()); const sourceTaskData = await addTask( 'Active/Standby Switch', TaskStatusType.NEW, @@ -1590,16 +1607,16 @@ export const switchActiveStandby: ResolverFn = async ( event: 'api:switchActiveStandby', payload: { ...auditLog, - } + }, }); // queue the task to trigger the migration await createMiscTask({ key: 'task:activestandby', data }); // return the task id and remote id - var retData = { + const retData = { id: data.task.id, - environment: environmentStandbyId + environment: environmentStandbyId, }; return retData; } catch (error) { @@ -1609,7 +1626,7 @@ export const switchActiveStandby: ResolverFn = async ( '', 'api:switchActiveStandby', data, - `Failed to create active to standby task, reason: ${error}` + `Failed to create active to standby task, reason: ${error}`, ); return `Error: ${error.message}`; } @@ -1618,10 +1635,11 @@ export const switchActiveStandby: ResolverFn = async ( export const bulkDeployEnvironmentLatest: ResolverFn = async ( _root, { input: { environments: environmentsInput, buildVariables, name: bulkName } }, - { keycloakGrant, models, sqlClientPool, hasPermission, userActivityLogger, keycloakUsersGroups } + { + keycloakGrant, models, sqlClientPool, hasPermission, userActivityLogger, keycloakUsersGroups, + }, ) => { - - /* + /* use the same mechanism for viewing all projects bulk deployments can span multiple projects, and anyone with access to a project will have the same rbac as `deployment:view` which covers all possible roles for a user @@ -1632,7 +1650,7 @@ export const bulkDeployEnvironmentLatest: ResolverFn = async ( let userProjectIds: number[]; try { await hasPermission('project', 'viewAll'); - } catch (err) { + } catch (_err) { if (!keycloakGrant) { logger.debug('No grant available for bulkDeployEnvironmentLatest'); return []; @@ -1642,7 +1660,7 @@ export const bulkDeployEnvironmentLatest: ResolverFn = async ( userProjectIds = getUserProjectIdsFromRoleProjectIds(userProjectRoles); } - let bulkId = uuid4(); + const bulkId = uuid4(); if (R.isEmpty(environmentsInput)) { throw new Error('You must provide environments'); @@ -1657,13 +1675,13 @@ export const bulkDeployEnvironmentLatest: ResolverFn = async ( // do a permission check on each environment being deployed if the user has a keycloak token (ie, if userProjectIds is populated) for (const envInput of environmentsInput) { if (userProjectIds) { - let environmentInput = envInput.environment + const environmentInput = envInput.environment; const environments = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentsByEnvironmentInput(environmentInput); const activeEnvironments = R.filter( - R.propEq('deleted', '0000-00-00 00:00:00'), - environments + R.propEq('0000-00-00 00:00:00', 'deleted'), + environments, ); if (activeEnvironments.length < 1 || activeEnvironments.length > 1) { @@ -1672,22 +1690,20 @@ export const bulkDeployEnvironmentLatest: ResolverFn = async ( const environment = R.prop(0, activeEnvironments); const project = await projectHelpers(sqlClientPool).getProjectById( - environment.project + environment.project, ); // if this is a keycloak token, check that the environments being deployed the user has access to await hasPermission('environment', `deploy:${environment.environmentType}`, { - project: project.id + project: project.id, }); } } // add the priority field if it doesn't exist - let envs = environmentsInput.map(x => ({ priority: 3, ...x})) + const envs = environmentsInput.map(x => ({ priority: 3, ...x })); // sort the environments - envs.sort(function(a, b) { - return b.priority - a.priority; - }); + envs.sort((a, b) => b.priority - a.priority); // otherwise if all the access is fine, then send the action to the actions-handler to be processed for (const envInput of envs) { @@ -1696,7 +1712,7 @@ export const bulkDeployEnvironmentLatest: ResolverFn = async ( let buildPriority = 3; // check if the priority has come through for a specific environment if (envInput.priority != null) { - buildPriority = envInput.priority + buildPriority = envInput.priority; } // check for buildvariables being passed in from the bulk deploy @@ -1706,23 +1722,23 @@ export const bulkDeployEnvironmentLatest: ResolverFn = async ( let newBuildVariables = buildVariables || []; if (envInput.buildVariables != null && buildVariables != null) { - newBuildVariables = jsonMerge(buildVariables, envInput.buildVariables, "name") + newBuildVariables = jsonMerge(buildVariables, envInput.buildVariables, 'name'); } else if (envInput.buildVariables != null) { - newBuildVariables = envInput.buildVariables + newBuildVariables = envInput.buildVariables; } const actionData = { - type: "deployEnvironmentLatest", - eventType: "bulkDeployment", + type: 'deployEnvironmentLatest', + eventType: 'bulkDeployment', data: { ...envInput, - bulkId: bulkId, - bulkName: bulkName, + bulkId, + bulkName, priority: buildPriority, - buildVariables: newBuildVariables - } - } - sendToLagoonActions("deployEnvironmentLatest", actionData) + buildVariables: newBuildVariables, + }, + }; + sendToLagoonActions('deployEnvironmentLatest', actionData); } const auditLog: AuditLog = { @@ -1734,17 +1750,17 @@ export const bulkDeployEnvironmentLatest: ResolverFn = async ( }; // @TODO: bulk deployments need some thought about audit logs that would be accessible to users // create an event for every environment in the deployment would be wasteful, but maybe consolidating by organization only? - userActivityLogger(`User performed a bulk deployment`, { + userActivityLogger('User performed a bulk deployment', { payload: { - bulkId: bulkId, - bulkName: bulkName, + bulkId, + bulkName, ...auditLog, - } + }, }); - return bulkId + return bulkId; }; export const deploymentSubscriber = createEnvironmentFilteredSubscriber([ - EVENTS.DEPLOYMENT + EVENTS.DEPLOYMENT, ]); diff --git a/services/api/src/resources/deployment/sql.ts b/services/api/src/resources/deployment/sql.ts index 94733187c2..a0c04e5291 100644 --- a/services/api/src/resources/deployment/sql.ts +++ b/services/api/src/resources/deployment/sql.ts @@ -1,16 +1,14 @@ -import { DeploymentSourceType } from '@lagoon/commons/dist/types'; +import { DeploymentSourceType } from '../../commons/types'; import { knex } from '../../util/db'; export const Sql = { - selectDeployment: (id: number) => - knex('deployment') - .where('id', '=', id) - .toString(), - selectDeploymentByNameAndEnvironment: (name: string, environmentId: number) => - knex('deployment') - .where('name', '=', name) - .andWhere('environment', '=', environmentId) - .toString(), + selectDeployment: (id: number) => knex('deployment') + .where('id', '=', id) + .toString(), + selectDeploymentByNameAndEnvironment: (name: string, environmentId: number) => knex('deployment') + .where('name', '=', name) + .andWhere('environment', '=', environmentId) + .toString(), insertDeployment: ({ id, name, @@ -43,118 +41,106 @@ export const Sql = { sourceType: DeploymentSourceType, sourceUser: string, buildType: string, - }) => - knex('deployment') - .insert({ - id, - name, - status, - created, - started, - completed, - environment, - remoteId, - priority, - bulkId, - bulkName, - buildStep, - sourceType, - sourceUser, - buildType, - }) - .toString(), - deleteDeployment: (id: number) => - knex('deployment') - .where('id', id) - .del() - .toString(), - updateDeployment: ({ id, patch }: { id: number, patch: { [key: string]: any } }) => - knex('deployment') - .where('id', id) - .update(patch) - .toString(), - selectPermsForDeployment: (id: number) => - knex('deployment') - .select({ pid: 'environment.project' }) - .join('environment', 'deployment.environment', '=', 'environment.id') - .where('deployment.id', id) - .toString(), + }) => knex('deployment') + .insert({ + id, + name, + status, + created, + started, + completed, + environment, + remoteId, + priority, + bulkId, + bulkName, + buildStep, + sourceType, + sourceUser, + buildType, + }) + .toString(), + deleteDeployment: (id: number) => knex('deployment') + .where('id', id) + .del() + .toString(), + updateDeployment: ({ id, patch }: { id: number, patch: { [key: string]: any } }) => knex('deployment') + .where('id', id) + .update(patch) + .toString(), + selectPermsForDeployment: (id: number) => knex('deployment') + .select({ pid: 'environment.project' }) + .join('environment', 'deployment.environment', '=', 'environment.id') + .where('deployment.id', id) + .toString(), // this selects all deployments for the environment and returns everything outside of the requested retain value - selectDeploymentHistoryRetention: (environment: number, retain: number) => - knex('deployment') - .select('id','name','remote_id') - .where('environment', environment) - .whereNotIn('id', function() { - this.select('id') - .from(function() { - this.select('id') - .from('deployment') - .where('environment', environment) - .orderBy('id','desc') - .limit(retain) - .as('d') - }) - }) - .toString(), + selectDeploymentHistoryRetention: (environment: number, retain: number) => knex('deployment') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .whereNotIn('id', function () { + this.select('id') + .from(function () { + this.select('id') + .from('deployment') + .where('environment', environment) + .orderBy('id', 'desc') + .limit(retain) + .as('d'); + }); + }) + .toString(), // this selects all tasks for the environment and returns everything outside of the requested retain days value - selectDeploymentHistoryRetentionDays: (environment: number, retain: number) => - knex('deployment') - .select('id','name','remote_id') - .where('environment', environment) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' DAY')) - .toString(), + selectDeploymentHistoryRetentionDays: (environment: number, retain: number) => knex('deployment') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} DAY`)) + .toString(), // this selects all tasks for the environment and returns everything outside of the requested retain months value - selectDeploymentHistoryRetentionMonths: (environment: number, retain: number) => - knex('deployment') - .select('id','name','remote_id') - .where('environment', environment) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' MONTH')) - .toString(), + selectDeploymentHistoryRetentionMonths: (environment: number, retain: number) => knex('deployment') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} MONTH`)) + .toString(), // this selects all tasks for the environment and returns everything - selectDeploymentHistoryForEnvironment: (environment: number) => - knex('deployment') - .where('environment', '=', environment) - .toString(), + selectDeploymentHistoryForEnvironment: (environment: number) => knex('deployment') + .where('environment', '=', environment) + .toString(), // same as select, except it deletes all deployments for the environment outside of the requested retain value - deleteDeploymentHistory: (environment: number, retain: number) => - knex('deployment') - .select('id','name','remote_id') - .where('environment', environment) - .whereNotIn('id', function() { - this.select('id') - .from(function() { - this.select('id') - .from('deployment') - .where('environment', environment) - .orderBy('id','desc') - .limit(retain) - .as('d') - }) - }) - .delete() - .toString(), + deleteDeploymentHistory: (environment: number, retain: number) => knex('deployment') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .whereNotIn('id', function () { + this.select('id') + .from(function () { + this.select('id') + .from('deployment') + .where('environment', environment) + .orderBy('id', 'desc') + .limit(retain) + .as('d'); + }); + }) + .delete() + .toString(), // same as select, except it deletes all tasks for the environment outside of the requested retain value - deleteDeploymentHistoryDays: (environment: number, retain: number) => - knex('deployment') - .select('id','name','remote_id') - .where('environment', environment) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' DAY')) - .delete() - .toString(), + deleteDeploymentHistoryDays: (environment: number, retain: number) => knex('deployment') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} DAY`)) + .delete() + .toString(), // same as select, except it deletes all tasks for the environment outside of the requested retain value - deleteDeploymentHistoryMonths: (environment: number, retain: number) => - knex('deployment') - .select('id','name','remote_id') - .where('environment', environment) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' MONTH')) - .delete() - .toString(), + deleteDeploymentHistoryMonths: (environment: number, retain: number) => knex('deployment') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} MONTH`)) + .delete() + .toString(), // delete all deployments for environment - deleteDeploymentHistoryForEnvironment: (environment: number) => - knex('deployment') - .where('environment', '=', environment) - .delete() - .toString(), + deleteDeploymentHistoryForEnvironment: (environment: number) => knex('deployment') + .where('environment', '=', environment) + .delete() + .toString(), selectDeploymentsByFilter: ({ startDate, endDate, @@ -175,53 +161,52 @@ export const Sql = { includeDeleted?: boolean; limitPerEnvironment?: number; limit?: number; - }) => - knex - .select('*') - .from(function() { - this.select( + }) => knex + .select('*') + .from(function () { + this.select( 'deployment.*', 'environment.project as project_id', knex.raw('ROW_NUMBER() OVER (PARTITION BY deployment.environment ORDER BY deployment.created DESC) AS env_row_num'), ) - .from('deployment') - .innerJoin('environment', 'deployment.environment', 'environment.id') - .as('ranked_deployments') - .modify(function (queryBuilder) { - if (userProjectIds) { - queryBuilder = queryBuilder.whereIn('environment.project', userProjectIds); - } - // collect builds for a specific date range - if (startDate) { - queryBuilder = queryBuilder.where('deployment.created', '>=', startDate); - } - if (endDate) { - queryBuilder = queryBuilder.where('deployment.created', '<=', endDate); - } - // collect builds for specific environment type - if (environmentType) { - queryBuilder = queryBuilder.where('environment.environment_type', environmentType); - } - if(deployTargets) { - queryBuilder = queryBuilder.whereIn('environment.openshift', deployTargets); - } - queryBuilder = queryBuilder.whereIn('deployment.status', deploymentStatus); - // if includeDeleted is false, exclude deleted environments in the results (default) - if (!includeDeleted) { - queryBuilder = queryBuilder.where('environment.deleted', '=', '0000-00-00 00:00:00'); - } - queryBuilder = queryBuilder.orderByRaw('deployment.created DESC, deployment.name DESC'); - }) - }) - .modify(function (queryBuilder) { - // limit the number of deployments per environment - if (limitPerEnvironment) { - queryBuilder = queryBuilder.where('env_row_num', '<=', limitPerEnvironment); - } - if (limit) { - queryBuilder = queryBuilder.limit(limit); - } - queryBuilder = queryBuilder.orderBy('created', 'desc') - }) - .toString(), + .from('deployment') + .innerJoin('environment', 'deployment.environment', 'environment.id') + .as('ranked_deployments') + .modify((queryBuilder) => { + if (userProjectIds) { + queryBuilder = queryBuilder.whereIn('environment.project', userProjectIds); + } + // collect builds for a specific date range + if (startDate) { + queryBuilder = queryBuilder.where('deployment.created', '>=', startDate); + } + if (endDate) { + queryBuilder = queryBuilder.where('deployment.created', '<=', endDate); + } + // collect builds for specific environment type + if (environmentType) { + queryBuilder = queryBuilder.where('environment.environment_type', environmentType); + } + if (deployTargets) { + queryBuilder = queryBuilder.whereIn('environment.openshift', deployTargets); + } + queryBuilder = queryBuilder.whereIn('deployment.status', deploymentStatus); + // if includeDeleted is false, exclude deleted environments in the results (default) + if (!includeDeleted) { + queryBuilder = queryBuilder.where('environment.deleted', '=', '0000-00-00 00:00:00'); + } + queryBuilder.orderByRaw('deployment.created DESC, deployment.name DESC'); + }); + }) + .modify((queryBuilder) => { + // limit the number of deployments per environment + if (limitPerEnvironment) { + queryBuilder = queryBuilder.where('env_row_num', '<=', limitPerEnvironment); + } + if (limit) { + queryBuilder = queryBuilder.limit(limit); + } + queryBuilder.orderBy('created', 'desc'); + }) + .toString(), }; diff --git a/services/api/src/resources/deploytargetconfig/helpers.ts b/services/api/src/resources/deploytargetconfig/helpers.ts index b2f982e962..aa2f1a34d3 100644 --- a/services/api/src/resources/deploytargetconfig/helpers.ts +++ b/services/api/src/resources/deploytargetconfig/helpers.ts @@ -4,18 +4,14 @@ import { query } from '../../util/db'; import { Sql } from './sql'; export const Helpers = (sqlClientPool: Pool) => { - const aliasOpenshiftToK8s = (deploytargetConfigs: any[]) => { - return deploytargetConfigs.map(deploytargetConfigs => { - return { - ...deploytargetConfigs, - }; - }); - }; + const aliasOpenshiftToK8s = (deploytargetConfigs: any[]) => deploytargetConfigs.map(deploytargetConfigs => ({ + ...deploytargetConfigs, + })); const getDeployTargetConfigById = async (deploytargetID: number) => { const rows = await query( sqlClientPool, - Sql.selectDeployTargetConfigById(deploytargetID) + Sql.selectDeployTargetConfigById(deploytargetID), ); const withK8s = aliasOpenshiftToK8s(rows); return R.prop(0, withK8s); diff --git a/services/api/src/resources/deploytargetconfig/resolvers.ts b/services/api/src/resources/deploytargetconfig/resolvers.ts index 70c9c53522..0230bdedce 100644 --- a/services/api/src/resources/deploytargetconfig/resolvers.ts +++ b/services/api/src/resources/deploytargetconfig/resolvers.ts @@ -1,20 +1,20 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { Helpers as environmentHelpers } from '../environment/helpers'; -import { isPatchEmpty, query, knex } from '../../util/db'; +import { isPatchEmpty, query } from '../../util/db'; import { Helpers } from './helpers'; import { Sql } from './sql'; -import { Sql as EnvironmentSql } from '../environment/sql' +import { Sql as EnvironmentSql } from '../environment/sql'; import { Helpers as projectHelpers } from '../project/helpers'; import { Helpers as organizationHelpers } from '../organization/helpers'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; export const getDeployTargetConfigById = async ( root, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const deployTargetConfig = await Helpers(sqlClientPool).getDeployTargetConfigById(args.id); @@ -25,17 +25,18 @@ export const getDeployTargetConfigById = async ( // since deploytargetconfigs are associated to a project // re-use the existing `project:view` permissions check, since the same sorts of fields // are viewable by the same permissions at the project scope - await projectHelpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, deployTargetConfig.project, adminScopes) + await projectHelpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, deployTargetConfig.project, adminScopes); return deployTargetConfig; }; export const getDeployTargetConfigsByProjectId: ResolverFn = async ( - project, - args, - { sqlClientPool, hasPermission, keycloakGrant, models, adminScopes } + project, + args, + { + sqlClientPool, hasPermission, adminScopes, + }, ) => { - let pid = args.project; if (project) { pid = project.id; @@ -44,7 +45,7 @@ export const getDeployTargetConfigsByProjectId: ResolverFn = async ( // since deploytargetconfigs are associated to a project // re-use the existing `project:view` permissions check, since the same sorts of fields // are viewable by the same permissions at the project scope - await projectHelpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, pid, adminScopes) + await projectHelpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, pid, adminScopes); const rows = await query(sqlClientPool, Sql.selectDeployTargetConfigsByProjectId(pid)); @@ -56,12 +57,12 @@ export const getDeployTargetConfigsByProjectId: ResolverFn = async ( export const getDeployTargetConfigsByDeployTarget: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { - let oid = args.deployTarget; + const oid = args.deployTarget; // only admin can view all deployment targetconfigs for a specfic deploy target - await hasPermission('project', `viewAll`); + await hasPermission('project', 'viewAll'); const rows = await query(sqlClientPool, Sql.selectDeployTargetConfigsByDeployTarget(oid)); @@ -71,75 +72,73 @@ export const getDeployTargetConfigsByDeployTarget: ResolverFn = async ( // used to check if project within an organization has requested valid deploy target const checkProjectDeployTargetByOrg = async (projectId: number, deployTargetId: number, sqlClientPool: Pool) => { - const projectdata = await projectHelpers(sqlClientPool).getProjectById(projectId) + const projectdata = await projectHelpers(sqlClientPool).getProjectById(projectId); if (projectdata.organization != null) { - let validDeployTarget = false + let validDeployTarget = false; const deploytargets = await organizationHelpers(sqlClientPool).getDeployTargetsByOrganizationId(projectdata.organization); for (const dt of deploytargets) { - if (dt.dtid == deployTargetId) { - validDeployTarget = true + if (dt.dtid === deployTargetId) { + validDeployTarget = true; } } if (!validDeployTarget) { throw new Error('The provided deploytarget is not valid for this organization'); } } - return projectdata -} + return projectdata; +}; export const updateEnvironmentDeployTarget: ResolverFn = async ( root, input, - utils + utils, ) => { - const { environment, deployTarget } = input; const { sqlClientPool, hasPermission, userActivityLogger } = utils; - let environmentObj = await environmentHelpers( - sqlClientPool + const environmentObj = await environmentHelpers( + sqlClientPool, ).getEnvironmentById(parseInt(environment)); - await hasPermission('project', 'update', { - project: environmentObj.project + project: environmentObj.project, }); // check the project has an organization id, if it does, check that the organization supports the requested deploytarget - const projectData = await checkProjectDeployTargetByOrg(environmentObj.project, deployTarget, sqlClientPool) + const projectData = await checkProjectDeployTargetByOrg(environmentObj.project, deployTarget, sqlClientPool); - const deployTargets = await getDeployTargetConfigsByProjectId(null, {project: environmentObj.project}, utils); + const deployTargets = await getDeployTargetConfigsByProjectId(null, { project: environmentObj.project }, utils); let matchesRule = false; - let ruleMatch = null; - if(deployTargets.length > 0) { + const _ruleMatch = null; + if (deployTargets.length > 0) { let endLoop = false; - for(let i = 0; i < deployTargets.length && !endLoop; i++) { - let branchTarget = deployTargets[i].branches; - switch(branchTarget) { - case(undefined): - case(null): - case(true): - case(false): + for (let i = 0; i < deployTargets.length && !endLoop; i++) { + const branchTarget = deployTargets[i].branches; + switch (branchTarget) { + case (undefined): + case (null): + case (true): + case (false): // if any of these before a match, we're actually done // because we don't have a _specific_ rule endLoop = true; continue; - break; + break; default: - let branchRegex = new RegExp(branchTarget); - if(branchRegex.test(branchTarget)) { - matchesRule = deployTargets[i].deployTarget == deployTarget; + const branchRegex = new RegExp(branchTarget); + if (branchRegex.test(branchTarget)) { + matchesRule = deployTargets[i].deployTarget === deployTarget; endLoop = true; } - //if there's no match for this first target, - //we continue on since there may be one at lighter weights + // if there's no match for this first target, + // we continue on since there may be one at lighter weights continue; } } } - if(matchesRule == false) { - throw new Error("Cannot change deploy target without matching Deploy Target rule"); + if (matchesRule === false) { + throw new Error('Cannot change deploy target without matching Deploy Target rule'); } await query( @@ -148,8 +147,8 @@ export const updateEnvironmentDeployTarget: ResolverFn = async ( id: environment, patch: { openshift: deployTarget, - } - }) + }, + }), ); const auditLog: AuditLog = { @@ -162,49 +161,47 @@ export const updateEnvironmentDeployTarget: ResolverFn = async ( id: deployTarget.toString(), type: AuditType.DEPLOYTARGET, }, - } + }; if (projectData.organization) { auditLog.organizationId = projectData.organization; } - userActivityLogger(`User changed DeployTarget for environment`, { + userActivityLogger('User changed DeployTarget for environment', { project: '', event: 'api:updateEnvironmentDeployTarget', payload: { ...input, ...auditLog, - } + }, }); return await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(parseInt(environment)); -} - +}; export const addDeployTargetConfig: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - - const deployTarget = input.deployTarget ; + const deployTarget = input.deployTarget; if (!deployTarget) { throw new Error('Must provide deployTarget field'); } - let id = input.id - let project = input.project - let weight = input.weight || 1 - let branches = input.branches || "true" - let pullrequests = input.pullrequests || "true" + const id = input.id; + const project = input.project; + const weight = input.weight || 1; + const branches = input.branches || 'true'; + const pullrequests = input.pullrequests || 'true'; // since deploytargetconfigs are associated to a project // re-use the existing `project:update` permissions check, since the same sorts of fields // are updateable by the same permissions at the project scope - await projectHelpers(sqlClientPool).checkOrgProjectUpdatePermission(hasPermission, project) + await projectHelpers(sqlClientPool).checkOrgProjectUpdatePermission(hasPermission, project); // check the project has an organization id, if it does, check that the organization supports the requested deploytarget - const projectData = await checkProjectDeployTargetByOrg(project, deployTarget, sqlClientPool) + const projectData = await checkProjectDeployTargetByOrg(project, deployTarget, sqlClientPool); const { insertId } = await query( sqlClientPool, @@ -214,14 +211,14 @@ export const addDeployTargetConfig: ResolverFn = async ( weight, deployTarget, branches, - pullrequests - }) + pullrequests, + }), ); // patch the project with the message to inform a user that their project is now using deploytarget configurations // this is an alpha feature with no UI support, so we do this for now // @TODO: if this feature comes out of alpha, a UI update will need to be done to show the changes properly - const projectRegex = await query(sqlClientPool, Sql.updateProjectBranchPullrequestRegex(project)); + const _projectRegex = await query(sqlClientPool, Sql.updateProjectBranchPullrequestRegex(project)); const rows = await query(sqlClientPool, Sql.selectDeployTargetConfigById(insertId)); @@ -239,19 +236,19 @@ export const addDeployTargetConfig: ResolverFn = async ( branches, pullrequests, deployTarget, - })}` + })}`, }, - } + }; if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User added DeployTargetConfig`, { + userActivityLogger('User added DeployTargetConfig', { project: '', event: 'api:addDeployTargetConfig', payload: { ...input, ...auditLog, - } + }, }); const deployTargetConfig = rows[0]; @@ -262,22 +259,21 @@ export const addDeployTargetConfig: ResolverFn = async ( export const deleteDeployTargetConfig: ResolverFn = async ( root, { input: { id, project } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - // since deploytargetconfigs are associated to a project // re-use the existing `project:update` permissions check, since the same sorts of fields // are updateable by the same permissions at the project scope // deleting a deploytargetconfig from a project is classed as updating the project - await projectHelpers(sqlClientPool).checkOrgProjectUpdatePermission(hasPermission, project) + await projectHelpers(sqlClientPool).checkOrgProjectUpdatePermission(hasPermission, project); const projectData = await projectHelpers( - sqlClientPool + sqlClientPool, ).getProjectById(project); try { - await query(sqlClientPool, Sql.deleteDeployTargetConfigById(id)); - } catch (err) { - // Not allowed to stop execution. + await query(sqlClientPool, Sql.deleteDeployTargetConfigById(id)); + } catch (_err) { + // Not allowed to stop execution. } const auditLog: AuditLog = { @@ -290,18 +286,18 @@ export const deleteDeployTargetConfig: ResolverFn = async ( id: id.toString(), type: AuditType.DEPLOYTARGETCONFIG, }, - } + }; if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User deleted DeployTargetConfig'`, { + userActivityLogger('User deleted DeployTargetConfig\'', { project: '', event: 'api:deleteDeployTargetConfig', payload: { id, project, ...auditLog, - } + }, }); return 'success'; @@ -340,8 +336,7 @@ export const updateDeployTargetConfig: ResolverFn = async ( } = patch; // get the projected id for a deploy config so permissions can be checked - const deployTargetConfig = - await Helpers(sqlClientPool).getDeployTargetConfigById(id); + const deployTargetConfig = await Helpers(sqlClientPool).getDeployTargetConfigById(id); if (!deployTargetConfig) { return null; @@ -356,7 +351,7 @@ export const updateDeployTargetConfig: ResolverFn = async ( ); // check the project has an organization id, if it does, check that the organization supports the requested deploytarget - const projectData = await checkProjectDeployTargetByOrg(deployTargetConfig.project, deployTarget, sqlClientPool) + const projectData = await checkProjectDeployTargetByOrg(deployTargetConfig.project, deployTarget, sqlClientPool); await query( sqlClientPool, @@ -388,18 +383,18 @@ export const updateDeployTargetConfig: ResolverFn = async ( branches, pullrequests, deployTarget, - })}` + })}`, }, - } + }; if (projectData.organization) { auditLog.organizationId = projectData.organization; } - userActivityLogger(`User updated DeployTargetConfig`, { + userActivityLogger('User updated DeployTargetConfig', { event: 'api:updateDeployTargetConfig', payload: { data: withK8s, ...auditLog, - } + }, }); return R.prop(0, withK8s); diff --git a/services/api/src/resources/deploytargetconfig/sql.ts b/services/api/src/resources/deploytargetconfig/sql.ts index 71cfcc3370..2769667103 100644 --- a/services/api/src/resources/deploytargetconfig/sql.ts +++ b/services/api/src/resources/deploytargetconfig/sql.ts @@ -7,46 +7,40 @@ export const Sql = { }; return knex('deploy_target_config') - .where('id', '=', id) - .update(updatePatch) - .toString(); - }, - selectDeployTargetConfigById: (id: number) => - knex('deploy_target_config') - .where('id', '=', id) - .toString(), - truncateDeployTargetConfigs: () => - knex('deploy_target_config') - .truncate() - .toString(), - updateProjectBranchPullrequestRegex: (id: number) => - knex('project') - .update('branches', 'This project is configured with DeployTargets') - .update('pullrequests', 'This project is configured with DeployTargets') .where('id', '=', id) - .toString(), - selectDeployTargetConfigsByProjectId: (id: number) => - knex('deploy_target_config') - .where('project', '=', id) - .orderBy('weight', 'desc') - .toString(), - selectDeployTargetConfigsByDeployTarget: (id: number) => - knex('deploy_target_config AS d') - .select('d.*') - .where('deploy_target', '=', id) - .toString(), - deleteDeployTargetConfigById: (id: number) => - knex('deploy_target_config') - .where('id', id) - .del() - .toString(), + .update(updatePatch) + .toString(); + }, + selectDeployTargetConfigById: (id: number) => knex('deploy_target_config') + .where('id', '=', id) + .toString(), + truncateDeployTargetConfigs: () => knex('deploy_target_config') + .truncate() + .toString(), + updateProjectBranchPullrequestRegex: (id: number) => knex('project') + .update('branches', 'This project is configured with DeployTargets') + .update('pullrequests', 'This project is configured with DeployTargets') + .where('id', '=', id) + .toString(), + selectDeployTargetConfigsByProjectId: (id: number) => knex('deploy_target_config') + .where('project', '=', id) + .orderBy('weight', 'desc') + .toString(), + selectDeployTargetConfigsByDeployTarget: (id: number) => knex('deploy_target_config AS d') + .select('d.*') + .where('deploy_target', '=', id) + .toString(), + deleteDeployTargetConfigById: (id: number) => knex('deploy_target_config') + .where('id', id) + .del() + .toString(), insertDeployTargetConfig: ({ id, project, weight, deployTarget, branches, - pullrequests + pullrequests, }: { id: number, project: number, @@ -54,15 +48,14 @@ export const Sql = { deployTarget: number, branches: number, pullrequests: string - }) => - knex('deploy_target_config') - .insert({ - id, - project, - weight, - deployTarget, - branches, - pullrequests - }) - .toString(), + }) => knex('deploy_target_config') + .insert({ + id, + project, + weight, + deployTarget, + branches, + pullrequests, + }) + .toString(), }; diff --git a/services/api/src/resources/env-variables/resolvers.ts b/services/api/src/resources/env-variables/resolvers.ts index 4f5b2e8fbc..dc57a21f61 100644 --- a/services/api/src/resources/env-variables/resolvers.ts +++ b/services/api/src/resources/env-variables/resolvers.ts @@ -1,13 +1,12 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { query, knex } from '../../util/db'; import { Sql } from './sql'; import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; import { Helpers as orgHelpers } from '../organization/helpers'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog, AuditResource } from '../audit/types'; -import {logger} from "../../loggers/logger"; export enum EnvVarType { ORGANIZATION = 'organization', @@ -16,15 +15,15 @@ export enum EnvVarType { } const getEnvVarType = (envVars: { - organization?: String; - project?: String; - environment?: String; + organization?: string; + project?: string; + environment?: string; }): EnvVarType | Error => { if (envVars.organization && !envVars.project && !envVars.environment) { return EnvVarType.ORGANIZATION; - } else if (!envVars.organization && envVars.project && !envVars.environment) { + } if (!envVars.organization && envVars.project && !envVars.environment) { return EnvVarType.PROJECT; - } else if (!envVars.organization && envVars.project && envVars.environment) { + } if (!envVars.organization && envVars.project && envVars.environment) { return EnvVarType.ENVIRONMENT; } @@ -50,94 +49,90 @@ export const getEnvVarsByProjectId: ResolverFn = async ( { id: pid }, args, { sqlClientPool, hasPermission, adminScopes }, - info + info, ) => { // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { - const index = info.fieldNodes[0].selectionSet.selections.findIndex(item => item.name.value === "value"); - if (index != -1) { + const index = info.fieldNodes[0].selectionSet.selections.findIndex(item => item.name.value === 'value'); + if (index !== -1) { await hasPermission('env_var', 'project:viewValue', { - project: pid + project: pid, }); const rows = await query( sqlClientPool, - Sql.selectEnvVarsByProjectId(pid) - ); - - return rows; - } else { - await hasPermission('env_var', 'project:view', { - project: pid - }); - const rows = await query( - sqlClientPool, - Sql.selectEnvVarsWithoutValueByProjectId(pid) + Sql.selectEnvVarsByProjectId(pid), ); return rows; } - } else { + await hasPermission('env_var', 'project:view', { + project: pid, + }); const rows = await query( sqlClientPool, - Sql.selectEnvVarsByProjectId(pid) + Sql.selectEnvVarsWithoutValueByProjectId(pid), ); return rows; } -} + const rows = await query( + sqlClientPool, + Sql.selectEnvVarsByProjectId(pid), + ); + + return rows; +}; export const getEnvVarsByEnvironmentId: ResolverFn = async ( { id: eid }, args, { sqlClientPool, hasPermission, adminScopes }, - info + info, ) => { const environment = await environmentHelpers( - sqlClientPool - ).getEnvironmentById(eid) + sqlClientPool, + ).getEnvironmentById(eid); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { - const index = info.fieldNodes[0].selectionSet.selections.findIndex(item => item.name.value === "value"); - if (index != -1) { + const index = info.fieldNodes[0].selectionSet.selections.findIndex(item => item.name.value === 'value'); + if (index !== -1) { await hasPermission( 'env_var', `environment:viewValue:${environment.environmentType}`, { - project: environment.project - } - ); - - const rows = await query( - sqlClientPool, - Sql.selectEnvVarsByEnvironmentId(eid) + project: environment.project, + }, ); - return rows; - } else { - await hasPermission( - 'env_var', - `environment:view:${environment.environmentType}`, - { - project: environment.project - } - ); const rows = await query( sqlClientPool, - Sql.selectEnvVarsWithoutValueByEnvironmentId(eid) + Sql.selectEnvVarsByEnvironmentId(eid), ); return rows; } - } else { + await hasPermission( + 'env_var', + `environment:view:${environment.environmentType}`, + { + project: environment.project, + }, + ); const rows = await query( sqlClientPool, - Sql.selectEnvVarsByEnvironmentId(eid) + Sql.selectEnvVarsWithoutValueByEnvironmentId(eid), ); return rows; } -} + const rows = await query( + sqlClientPool, + Sql.selectEnvVarsByEnvironmentId(eid), + ); + + return rows; +}; export const deleteEnvVariableByName: ResolverFn = async ( root, @@ -168,10 +163,9 @@ export const deleteEnvVariableByName: ResolverFn = async ( let envVarTypeName = ''; let orgId; - if (envVarType == EnvVarType.ORGANIZATION) { + if (envVarType === EnvVarType.ORGANIZATION) { envVarTypeName = orgName; - const org = - await orgHelpers(sqlClientPool).getOrganizationByName(orgName); + const org = await orgHelpers(sqlClientPool).getOrganizationByName(orgName); const orgVariable = await query( sqlClientPool, Sql.selectEnvVarByNameAndOrgId(name, org.id), @@ -184,22 +178,21 @@ export const deleteEnvVariableByName: ResolverFn = async ( id: org.id.toString(), type: AuditType.ORGANIZATION, details: org.name, - } + }; orgId = org.id; if (orgVariable[0]) { - envVarScope = orgVariable[0].scope - envVarId = orgVariable[0].id - envVarName = orgVariable[0].name + envVarScope = orgVariable[0].scope; + envVarId = orgVariable[0].id; + envVarName = orgVariable[0].name; await query(sqlClientPool, Sql.deleteEnvVariable(orgVariable[0].id)); } else { // variable doesn't exist, just return success return 'success'; } } else { - const project = - await projectHelpers(sqlClientPool).getProjectByProjectInput({name: projectName}); + const project = await projectHelpers(sqlClientPool).getProjectByProjectInput({ name: projectName }); - if (envVarType == EnvVarType.PROJECT) { + if (envVarType === EnvVarType.PROJECT) { envVarTypeName = projectName; const projectVariable = await query( sqlClientPool, @@ -213,14 +206,14 @@ export const deleteEnvVariableByName: ResolverFn = async ( id: project.id.toString(), type: AuditType.PROJECT, details: project.name, - } + }; if (project.organization) { - orgId = project.organization + orgId = project.organization; } if (projectVariable[0]) { - envVarScope = projectVariable[0].scope - envVarId = projectVariable[0].id - envVarName = projectVariable[0].name + envVarScope = projectVariable[0].scope; + envVarId = projectVariable[0].id; + envVarName = projectVariable[0].name; await query( sqlClientPool, Sql.deleteEnvVariable(projectVariable[0].id), @@ -229,7 +222,7 @@ export const deleteEnvVariableByName: ResolverFn = async ( // variable doesn't exist, just return success return 'success'; } - } else if (envVarType == EnvVarType.ENVIRONMENT) { + } else if (envVarType === EnvVarType.ENVIRONMENT) { const environmentRows = await query( sqlClientPool, Sql.selectEnvironmentByNameAndProject(environmentName, project.id), @@ -252,14 +245,14 @@ export const deleteEnvVariableByName: ResolverFn = async ( id: environment.id.toString(), type: AuditType.ENVIRONMENT, details: environment.name, - } + }; if (project.organization) { - orgId = project.organization + orgId = project.organization; } if (environmentVariable[0]) { - envVarScope = environmentVariable[0].scope - envVarId = environmentVariable[0].id - envVarName = environmentVariable[0].name + envVarScope = environmentVariable[0].scope; + envVarId = environmentVariable[0].id; + envVarName = environmentVariable[0].name; envVarTypeName = environmentName; await query( sqlClientPool, @@ -281,7 +274,7 @@ export const deleteEnvVariableByName: ResolverFn = async ( } const auditLog: AuditLog = { - resource: resource, + resource, linkedResource: { id: envVarId.toString(), type: AuditType.VARIABLE, @@ -291,7 +284,7 @@ export const deleteEnvVariableByName: ResolverFn = async ( if (orgId) { auditLog.organizationId = orgId; } - userActivityLogger(`User deleted environment variable`, { + userActivityLogger('User deleted environment variable', { project: projectName, event: 'api:deleteEnvVariableByName', payload: { @@ -319,7 +312,7 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( }, { sqlClientPool, hasPermission, userActivityLogger }, ) => { - if (name.trim().length == 0) { + if (name.trim().length === 0) { throw new Error('A variable name must be provided.'); } @@ -339,15 +332,14 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( let resource: AuditResource; - let updateData = {}; + let updateData: Record = {}; let envVarTypeName = ''; - let envVarName = name.trim(); + const envVarName = name.trim(); let orgId; - if (envVarType == EnvVarType.ORGANIZATION) { + if (envVarType === EnvVarType.ORGANIZATION) { envVarTypeName = orgName; - const org = - await orgHelpers(sqlClientPool).getOrganizationByName(orgName); + const org = await orgHelpers(sqlClientPool).getOrganizationByName(orgName); await hasPermission('organization', 'addEnvVar', { organization: org.id, }); @@ -355,7 +347,7 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( id: org.id.toString(), type: AuditType.ORGANIZATION, details: org.name, - } + }; orgId = org.id; updateData = { name: envVarName, @@ -364,8 +356,7 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( organization: org.id, }; } else { - const project = - await projectHelpers(sqlClientPool).getProjectByProjectInput({name: projectName}); + const project = await projectHelpers(sqlClientPool).getProjectByProjectInput({ name: projectName }); if (envVarType === EnvVarType.PROJECT) { envVarTypeName = projectName; @@ -376,9 +367,9 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( id: project.id.toString(), type: AuditType.PROJECT, details: project.name, - } + }; if (project.organization) { - orgId = project.organization + orgId = project.organization; } updateData = { name: envVarName, @@ -386,7 +377,7 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( scope, project: project.id, }; - } else if (envVarType == EnvVarType.ENVIRONMENT) { + } else if (envVarType === EnvVarType.ENVIRONMENT) { envVarTypeName = environmentName; const environmentRows = await query( sqlClientPool, @@ -404,9 +395,9 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( id: environment.id.toString(), type: AuditType.ENVIRONMENT, details: environment.name, - } + }; if (project.organization) { - orgId = project.organization + orgId = project.organization; } updateData = { name: envVarName, @@ -418,7 +409,7 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( } // Let's set the updated value for the env var - updateData['updated'] = knex.fn.now(); + updateData.updated = knex.fn.now(); const createOrUpdateSql = knex('env_vars') .insert({ @@ -435,7 +426,7 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( const rows = await query(sqlClientPool, Sql.selectEnvVariable(insertId)); const auditLog: AuditLog = { - resource: resource, + resource, linkedResource: { id: insertId.toString(), type: AuditType.VARIABLE, @@ -466,92 +457,88 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async ( export const getEnvVariablesByProjectEnvironmentName: ResolverFn = async ( root, { input: { project: projectName, environment: environmentName } }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes }, - info + { + sqlClientPool, hasPermission, adminScopes, + }, + info, ) => { - const index = info.fieldNodes[0].selectionSet.selections.findIndex(item => item.name.value === "value"); + const index = info.fieldNodes[0].selectionSet.selections.findIndex(item => item.name.value === 'value'); const project = await projectHelpers(sqlClientPool).getProjectByProjectInput( - {name: projectName} + { name: projectName }, ); if (environmentName) { // is environment const environmentRows = await query( sqlClientPool, - Sql.selectEnvironmentByNameAndProject(environmentName, project.id) + Sql.selectEnvironmentByNameAndProject(environmentName, project.id), ); const environment = environmentRows[0]; // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { - if (index != -1) { + if (index !== -1) { await hasPermission( 'env_var', `environment:viewValue:${environment.environmentType}`, { - project: project.id - } - ); - - const environmentVariables = await query( - sqlClientPool, - Sql.selectEnvVarsByEnvironmentId(environment.id) - ); - return environmentVariables - - } else { - await hasPermission( - 'env_var', - `environment:view:${environment.environmentType}`, - { - project: project.id - } + project: project.id, + }, ); const environmentVariables = await query( sqlClientPool, - Sql.selectEnvVarsWithoutValueByEnvironmentId(environment.id) + Sql.selectEnvVarsByEnvironmentId(environment.id), ); - return environmentVariables + return environmentVariables; } - } else { + await hasPermission( + 'env_var', + `environment:view:${environment.environmentType}`, + { + project: project.id, + }, + ); + const environmentVariables = await query( sqlClientPool, - Sql.selectEnvVarsByEnvironmentId(environment.id) + Sql.selectEnvVarsWithoutValueByEnvironmentId(environment.id), ); - return environmentVariables + return environmentVariables; } - } else if (projectName) { + const environmentVariables = await query( + sqlClientPool, + Sql.selectEnvVarsByEnvironmentId(environment.id), + ); + return environmentVariables; + } if (projectName) { // is project // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { - if (index != -1) { + if (index !== -1) { await hasPermission('env_var', 'project:viewValue', { - project: project.id - }); - const projectVariables = await query( - sqlClientPool, - Sql.selectEnvVarsByProjectId(project.id) - ); - return projectVariables - - } else { - await hasPermission('env_var', 'project:view', { - project: project.id + project: project.id, }); const projectVariables = await query( sqlClientPool, - Sql.selectEnvVarsWithoutValueByProjectId(project.id) + Sql.selectEnvVarsByProjectId(project.id), ); - return projectVariables + return projectVariables; } - } else { + await hasPermission('env_var', 'project:view', { + project: project.id, + }); const projectVariables = await query( sqlClientPool, - Sql.selectEnvVarsByProjectId(project.id) + Sql.selectEnvVarsWithoutValueByProjectId(project.id), ); - return projectVariables + return projectVariables; } + const projectVariables = await query( + sqlClientPool, + Sql.selectEnvVarsByProjectId(project.id), + ); + return projectVariables; } return []; }; @@ -559,7 +546,7 @@ export const getEnvVariablesByProjectEnvironmentName: ResolverFn = async ( // Deprecated export const addEnvVariable: ResolverFn = async (obj, args, context) => { const { - input: { type } + input: { type }, } = args; if (args.input.scope === 'internal_container_registry') { @@ -568,7 +555,7 @@ export const addEnvVariable: ResolverFn = async (obj, args, context) => { if (type === 'project') { return addEnvVariableToProject(obj, args, context); - } else if (type === 'environment') { + } if (type === 'environment') { return addEnvVariableToEnvironment(obj, args, context); } }; @@ -576,14 +563,17 @@ export const addEnvVariable: ResolverFn = async (obj, args, context) => { // Deprecated const addEnvVariableToProject = async ( root, - { input: { id, typeId, name, value, scope } }, - { sqlClientPool, hasPermission, userActivityLogger } + { + input: { + id, typeId, name, value, scope, + }, + }, + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('env_var', 'project:add', { - project: `${typeId}` + project: `${typeId}`, }); - const project = - await projectHelpers(sqlClientPool).getProjectByProjectInput({id: typeId}); + const project = await projectHelpers(sqlClientPool).getProjectByProjectInput({ id: typeId }); const { insertId } = await query( sqlClientPool, @@ -592,8 +582,8 @@ const addEnvVariableToProject = async ( name, value, scope, - project: typeId - }) + project: typeId, + }), ); const rows = await query(sqlClientPool, Sql.selectEnvVariable(insertId)); @@ -622,7 +612,7 @@ const addEnvVariableToProject = async ( scope, typeId, ...auditLog, - } + }, }); return R.prop(0, rows); @@ -631,19 +621,23 @@ const addEnvVariableToProject = async ( // Deprecated const addEnvVariableToEnvironment = async ( root, - { input: { id, typeId, name, value, scope } }, - { sqlClientPool, hasPermission, userActivityLogger } + { + input: { + id, typeId, name, value, scope, + }, + }, + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(typeId); await hasPermission( 'env_var', `environment:add:${environment.environmentType}`, { - project: environment.project - } + project: environment.project, + }, ); const project = await projectHelpers(sqlClientPool).getProjectByEnvironmentId(environment.id); @@ -654,8 +648,8 @@ const addEnvVariableToEnvironment = async ( name, value, scope, - environment: typeId - }) + environment: typeId, + }), ); const rows = await query(sqlClientPool, Sql.selectEnvVariable(insertId)); @@ -685,7 +679,7 @@ const addEnvVariableToEnvironment = async ( typeId, environment, ...auditLog, - } + }, }); return R.prop(0, rows); @@ -695,12 +689,12 @@ const addEnvVariableToEnvironment = async ( export const deleteEnvVariable: ResolverFn = async ( root, { input: { id } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const perms = await query(sqlClientPool, Sql.selectPermsForEnvVariable(id)); await hasPermission('env_var', 'delete', { - project: R.path(['0', 'pid'], perms) + project: R.path(['0', 'pid'], perms), }); let resource: AuditResource; @@ -708,8 +702,7 @@ export const deleteEnvVariable: ResolverFn = async ( const rows = await query(sqlClientPool, Sql.selectEnvVarById(id)); const envVar = R.prop(0, rows); if (envVar.organization) { - const org = - await orgHelpers(sqlClientPool).getOrganizationById(envVar.organization); + const org = await orgHelpers(sqlClientPool).getOrganizationById(envVar.organization); resource = { id: org.id.toString(), type: AuditType.ORGANIZATION, @@ -717,35 +710,32 @@ export const deleteEnvVariable: ResolverFn = async ( }; orgId = org.id; } else if (envVar.project) { - const project = - await projectHelpers(sqlClientPool).getProjectById(envVar.project); + const project = await projectHelpers(sqlClientPool).getProjectById(envVar.project); resource = { id: project.id.toString(), type: AuditType.PROJECT, details: project.name, }; if (project.organization) { - orgId = project.organization + orgId = project.organization; } } else if (envVar.environment) { - const environment = - await environmentHelpers(sqlClientPool).getEnvironmentById(envVar.environment); - const project = - await projectHelpers(sqlClientPool).getProjectById(environment.project); + const environment = await environmentHelpers(sqlClientPool).getEnvironmentById(envVar.environment); + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); resource = { id: environment.id.toString(), type: AuditType.ENVIRONMENT, details: environment.name, }; if (project.organization) { - orgId = project.organization + orgId = project.organization; } } await query(sqlClientPool, Sql.deleteEnvVariable(id)); const auditLog: AuditLog = { - resource: resource, + resource, linkedResource: { id: envVar.id.toString(), type: AuditType.VARIABLE, @@ -755,13 +745,13 @@ export const deleteEnvVariable: ResolverFn = async ( if (orgId) { auditLog.organizationId = orgId; } - userActivityLogger(`User deleted environment variable`, { + userActivityLogger('User deleted environment variable', { project: '', event: 'api:deleteEnvVariable', payload: { id, ...auditLog, - } + }, }); return 'success'; diff --git a/services/api/src/resources/env-variables/sql.ts b/services/api/src/resources/env-variables/sql.ts index 554b5b3ff1..f12d748327 100644 --- a/services/api/src/resources/env-variables/sql.ts +++ b/services/api/src/resources/env-variables/sql.ts @@ -1,11 +1,10 @@ import { knex } from '../../util/db'; export const Sql = { - selectEnvVariable: (id: number) => - knex('env_vars') - .select('id', 'name', 'value', 'scope') - .where('id', '=', id) - .toString(), + selectEnvVariable: (id: number) => knex('env_vars') + .select('id', 'name', 'value', 'scope') + .where('id', '=', id) + .toString(), insertEnvVariable: ({ id, name, @@ -20,89 +19,76 @@ export const Sql = { scope: string, project?: number, environment?: number, - }) => - knex('env_vars') - .insert({ - id, - name, - value, - scope, - project, - environment, - }) - .toString(), - deleteEnvVariable: (id: number) => - knex('env_vars') - .where('id', id) - .del() - .toString(), - selectEnvironmentByNameAndProject: (name: string, projectId: number) => - knex('environment') - .where('name', '=', name) - .andWhere('project', '=', projectId) - .andWhere('deleted', '0000-00-00 00:00:00') - .toString(), - selectEnvVarByNameAndOrgId: (name: string, orgId: number) => - knex('env_vars') - .select('env_vars.*') - .where('env_vars.name', '=', name) - .andWhere('env_vars.organization', '=', orgId) - .toString(), - selectEnvVarByNameAndProjectId: (name: string, projectId: number) => - knex('env_vars') - .select('env_vars.*') - .where('env_vars.name', '=', name) - .andWhere('env_vars.project', '=', projectId) - .toString(), - selectEnvVarByNameAndEnvironmentId: (name: string, environmentId: number) => - knex('env_vars') - .select('env_vars.*') - .where('env_vars.name', '=', name) - .andWhere('env_vars.environment', '=', environmentId) - .toString(), - selectEnvVarsByOrgId: (orgId: number) => - knex('env_vars') - .select('env_vars.*') - .where('env_vars.organization', '=', orgId) - .orderBy('env_vars.name', 'asc') - .toString(), - selectEnvVarsByProjectId: (projectId: number) => - knex('env_vars') - .select('env_vars.*') - .where('env_vars.project', '=', projectId) - .orderBy('env_vars.name', 'asc') - .toString(), - selectEnvVarsWithoutValueByProjectId: (projectId: number) => - knex('env_vars') - .select('id', 'name', 'scope') - .where('env_vars.project', '=', projectId) - .orderBy('env_vars.name', 'asc') - .toString(), - selectEnvVarsByEnvironmentId: (environmentId: number) => - knex('env_vars') - .select('env_vars.*') - .where('env_vars.environment', '=', environmentId) - .orderBy('env_vars.name', 'asc') - .toString(), - selectEnvVarsWithoutValueByEnvironmentId: (environmentId: number) => - knex('env_vars') - .select('id', 'name', 'scope') - .where('env_vars.environment', '=', environmentId) - .orderBy('env_vars.name', 'asc') - .toString(), - selectEnvVarById: (id: number) => - knex('env_vars') - .where('id', '=', id) - .toString(), - selectPermsForEnvVariable: (id: number) => - knex('env_vars') - .select({ pid: 'project.id' }) - .leftJoin('environment', 'env_vars.environment', '=', 'environment.id') - .leftJoin('project', function () { - this - .on('env_vars.project', '=', 'project.id') - .orOn('environment.project', '=', 'project.id') - }) - .where('env_vars.id', id) - .toString(), + }) => knex('env_vars') + .insert({ + id, + name, + value, + scope, + project, + environment, + }) + .toString(), + deleteEnvVariable: (id: number) => knex('env_vars') + .where('id', id) + .del() + .toString(), + selectEnvironmentByNameAndProject: (name: string, projectId: number) => knex('environment') + .where('name', '=', name) + .andWhere('project', '=', projectId) + .andWhere('deleted', '0000-00-00 00:00:00') + .toString(), + selectEnvVarByNameAndOrgId: (name: string, orgId: number) => knex('env_vars') + .select('env_vars.*') + .where('env_vars.name', '=', name) + .andWhere('env_vars.organization', '=', orgId) + .toString(), + selectEnvVarByNameAndProjectId: (name: string, projectId: number) => knex('env_vars') + .select('env_vars.*') + .where('env_vars.name', '=', name) + .andWhere('env_vars.project', '=', projectId) + .toString(), + selectEnvVarByNameAndEnvironmentId: (name: string, environmentId: number) => knex('env_vars') + .select('env_vars.*') + .where('env_vars.name', '=', name) + .andWhere('env_vars.environment', '=', environmentId) + .toString(), + selectEnvVarsByOrgId: (orgId: number) => knex('env_vars') + .select('env_vars.*') + .where('env_vars.organization', '=', orgId) + .orderBy('env_vars.name', 'asc') + .toString(), + selectEnvVarsByProjectId: (projectId: number) => knex('env_vars') + .select('env_vars.*') + .where('env_vars.project', '=', projectId) + .orderBy('env_vars.name', 'asc') + .toString(), + selectEnvVarsWithoutValueByProjectId: (projectId: number) => knex('env_vars') + .select('id', 'name', 'scope') + .where('env_vars.project', '=', projectId) + .orderBy('env_vars.name', 'asc') + .toString(), + selectEnvVarsByEnvironmentId: (environmentId: number) => knex('env_vars') + .select('env_vars.*') + .where('env_vars.environment', '=', environmentId) + .orderBy('env_vars.name', 'asc') + .toString(), + selectEnvVarsWithoutValueByEnvironmentId: (environmentId: number) => knex('env_vars') + .select('id', 'name', 'scope') + .where('env_vars.environment', '=', environmentId) + .orderBy('env_vars.name', 'asc') + .toString(), + selectEnvVarById: (id: number) => knex('env_vars') + .where('id', '=', id) + .toString(), + selectPermsForEnvVariable: (id: number) => knex('env_vars') + .select({ pid: 'project.id' }) + .leftJoin('environment', 'env_vars.environment', '=', 'environment.id') + .leftJoin('project', function () { + this + .on('env_vars.project', '=', 'project.id') + .orOn('environment.project', '=', 'project.id'); + }) + .where('env_vars.id', id) + .toString(), }; diff --git a/services/api/src/resources/environment/__snapshots__/environment.test.js.snap b/services/api/src/resources/environment/__snapshots__/environment.test.js.snap index 57f7118dbd..3cc81edf42 100644 --- a/services/api/src/resources/environment/__snapshots__/environment.test.js.snap +++ b/services/api/src/resources/environment/__snapshots__/environment.test.js.snap @@ -2,4 +2,5 @@ exports[`Sql selectEnvironment should create a proper query 1`] = `"select * from \`environment\` where \`id\` = 1"`; -exports[`Sql updateEnvironment should create a proper query 1`] = `"update \`environment\` set \`project\` = 1 where \`id\` = 1"`; +exports[`Sql updateEnvironment should create a proper query 1`] = `"update \`environment\` set \`project\` = 1, \`updated\` = CURRENT_TIMESTAMP where \`id\` = 1"`; + diff --git a/services/api/src/resources/environment/environment_redeploy.ts b/services/api/src/resources/environment/environment_redeploy.ts index d21c5b00ca..d108f6a68a 100644 --- a/services/api/src/resources/environment/environment_redeploy.ts +++ b/services/api/src/resources/environment/environment_redeploy.ts @@ -2,27 +2,25 @@ import { ResolverFn } from '..'; import { query } from '../../util/db'; - export const environmentPendingChangeTypes = { - ENVVAR: "ENVVAR", + ENVVAR: 'ENVVAR', }; -export const getPendingChangesByEnvironmentId: ResolverFn = async( -{ - id -}, -_, -{ sqlClientPool, hasPermission }, +export const getPendingChangesByEnvironmentId: ResolverFn = async ( + { + id, + }, + _, + { sqlClientPool }, ) => { - // Note: as it stands, the only pending changes we have now have to do - // with env vars, but anything can be added in the form - // {type:"string", details:"string", date: "string"} - let pendingChanges = await getPendingEnvVarChanges(sqlClientPool, id); - return pendingChanges; -} - -const getPendingEnvVarChanges = async(sqlClientPool, envId) => { + // Note: as it stands, the only pending changes we have now have to do + // with env vars, but anything can be added in the form + // {type:"string", details:"string", date: "string"} + const pendingChanges = await getPendingEnvVarChanges(sqlClientPool, id); + return pendingChanges; +}; +const getPendingEnvVarChanges = async (sqlClientPool, envId) => { const sql = ` WITH last_completed AS ( SELECT COALESCE(MAX(d.created), TIMESTAMP('1970-01-01 00:00:00')) AS ts @@ -92,34 +90,30 @@ const getPendingEnvVarChanges = async(sqlClientPool, envId) => { const overrideMap = new Map(); - const shouldDeployDBString = "deploy"; + const shouldDeployDBString = 'deploy'; results.forEach((row) => { - if (overrideMap.has(row.envvarName)) { // Check if there is already an instance of this var - let other = overrideMap.get(row.envvarName); - if(row.envvarPriority > other.envvarPriority) { // if this is higher - if(row.mustDeploy === shouldDeployDBString) { - // We override conventionally - overrideMap.set(row.envvarName, row); - } else { - // We override without any deployment - remove - overrideMap.delete(row.envvarName); - } - } - } else { - if(row.mustDeploy === shouldDeployDBString) { - // First instance of a var to be deployed - overrideMap.set(row.envvarName, row); - } + if (overrideMap.has(row.envvarName)) { // Check if there is already an instance of this var + const other = overrideMap.get(row.envvarName); + if (row.envvarPriority > other.envvarPriority) { // if this is higher + if (row.mustDeploy === shouldDeployDBString) { + // We override conventionally + overrideMap.set(row.envvarName, row); + } else { + // We override without any deployment - remove + overrideMap.delete(row.envvarName); + } } - }) + } else if (row.mustDeploy === shouldDeployDBString) { + // First instance of a var to be deployed + overrideMap.set(row.envvarName, row); + } + }); - const pendingChanges = Array.from(overrideMap.values()).map((row) => { - return { - type:environmentPendingChangeTypes.ENVVAR, - details: `Variable name: ${row.envvarName} (source: ${row.envvarSource})`, - date: row.envvarUpdated - }; - }) + const pendingChanges = Array.from(overrideMap.values()).map((row) => ({ + type: environmentPendingChangeTypes.ENVVAR, + details: `Variable name: ${row.envvarName} (source: ${row.envvarSource})`, + date: row.envvarUpdated, + })); return pendingChanges; -} \ No newline at end of file +}; diff --git a/services/api/src/resources/environment/helpers.ts b/services/api/src/resources/environment/helpers.ts index ed26a02e96..e584868cb7 100644 --- a/services/api/src/resources/environment/helpers.ts +++ b/services/api/src/resources/environment/helpers.ts @@ -1,6 +1,6 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; -import { asyncPipe } from '@lagoon/commons/dist/util/func'; +import { asyncPipe } from '../../commons/util/func'; import { query } from '../../util/db'; import { Sql } from './sql'; import { Sql as problemSql } from '../problem/sql'; @@ -12,19 +12,15 @@ import { HistoryRetentionEnforcer } from '../retentionpolicy/history'; import { logger } from '../../loggers/logger'; export const Helpers = (sqlClientPool: Pool) => { - const aliasOpenshiftToK8s = (environments: any[]) => { - return environments.map(environment => { - return { - ...environment, - kubernetesNamespaceName: environment.openshiftProjectName - }; - }); - }; + const aliasOpenshiftToK8s = (environments: any[]) => environments.map(environment => ({ + ...environment, + kubernetesNamespaceName: environment.openshiftProjectName, + })); const getEnvironmentById = async (environmentID: number) => { const rows = await query( sqlClientPool, - Sql.selectEnvironmentById(environmentID) + Sql.selectEnvironmentById(environmentID), ); const withK8s = aliasOpenshiftToK8s(rows); return R.prop(0, withK8s); @@ -40,22 +36,22 @@ export const Helpers = (sqlClientPool: Pool) => { // attempt to run any retention policy processes before the environment is deleted try { // export a dump of the project, environment data, and associated task and deployment history before the environment is deleted - await HistoryRetentionEnforcer().saveEnvironmentHistoryBeforeDeletion(projectData, environmentData) + await HistoryRetentionEnforcer().saveEnvironmentHistoryBeforeDeletion(projectData, environmentData); } catch (e) { - logger.error(`error running save environment history: ${e}`) + logger.error(`error running save environment history: ${e}`); } // purge all history for this environment, including logs and files from s3 try { // remove all deployments and associated files - await HistoryRetentionEnforcer().cleanupAllDeployments(projectData, environmentData) + await HistoryRetentionEnforcer().cleanupAllDeployments(projectData, environmentData); } catch (e) { - logger.error(`error running deployment retention enforcer: ${e}`) + logger.error(`error running deployment retention enforcer: ${e}`); } try { // remove all tasks and associated files - await HistoryRetentionEnforcer().cleanupAllTasks(projectData, environmentData) + await HistoryRetentionEnforcer().cleanupAllTasks(projectData, environmentData); } catch (e) { - logger.error(`error running task retention enforcer: ${e}`) + logger.error(`error running task retention enforcer: ${e}`); } // then proceed to purge related data @@ -64,42 +60,42 @@ export const Helpers = (sqlClientPool: Pool) => { // logger.debug(`deleting environment ${name}/id:${eid}/project:${pid} environment variables`) await query( sqlClientPool, - Sql.deleteEnvironmentVariables(eid) + Sql.deleteEnvironmentVariables(eid), ); // clean up service containers // logger.debug(`deleting environment ${name}/id:${eid}/project:${pid} environment service containers`) await query( sqlClientPool, Sql.deleteServiceContainersByEnvironmentId( - eid - ) + eid, + ), ); // clean up services // logger.debug(`deleting environment ${name}/id:${eid}/project:${pid} environment services`) await query( sqlClientPool, - Sql.deleteServices(eid) + Sql.deleteServices(eid), ); // Here we clean up insights attached to the environment // logger.debug(`deleting environment ${name}/id:${eid}/project:${pid} environment facts`) await query( sqlClientPool, - factSql.deleteFactsForEnvironment(eid) + factSql.deleteFactsForEnvironment(eid), ); // logger.debug(`deleting environment ${name}/id:${eid}/project:${pid} environment problems`) await query( sqlClientPool, - problemSql.deleteProblemsForEnvironment(eid) + problemSql.deleteProblemsForEnvironment(eid), ); // remove all route associations from environment // delete any autogenerated routes, these are not reusable and are only associated to the environment they are on - await routeHelpers(sqlClientPool).deleteAutogeneratedRoutesForEnvironment(eid) + await routeHelpers(sqlClientPool).deleteAutogeneratedRoutesForEnvironment(eid); // delete any lagoon.yml managed routes from the api, if they want to manage them in the api // they should be modified in the api to update the source to the api to nullify the lagoon.yml source - await routeHelpers(sqlClientPool).deleteLagoonYAMLRoutesForEnvironment(eid) + await routeHelpers(sqlClientPool).deleteLagoonYAMLRoutesForEnvironment(eid); // but leave the routes in the api attached to the project - await routeHelpers(sqlClientPool).removeAllRoutesFromEnvironment(eid) + await routeHelpers(sqlClientPool).removeAllRoutesFromEnvironment(eid); // delete any autogenerated route configuration for this environment await routeHelpers(sqlClientPool).deleteAutogeneratedRouteConfigForEnvironment(eid); @@ -124,28 +120,27 @@ export const Helpers = (sqlClientPool: Pool) => { // Sql.deleteEnvironmentStorageByEnvironmentId(eid) // ); } catch (e) { - logger.error(`error cleaning up linked environment tables: ${e}`) + logger.error(`error cleaning up linked environment tables: ${e}`); } // delete the environment // logger.debug(`deleting environment ${name}/id:${eid}/project:${pid}`) await query( sqlClientPool, - Sql.deleteEnvironment(name, pid) + Sql.deleteEnvironment(name, pid), ); - }, getEnvironmentsDeploytarget: async (eid) => { const rows = await query( sqlClientPool, - Sql.selectDeployTarget(eid) + Sql.selectDeployTarget(eid), ); return aliasOpenshiftToK8s(rows); }, getEnvironmentsByProjectId: async (projectId) => { const rows = await query( sqlClientPool, - Sql.selectEnvironmentsByProjectID(projectId) + Sql.selectEnvironmentsByProjectID(projectId), ); return aliasOpenshiftToK8s(rows); }, @@ -154,8 +149,8 @@ export const Helpers = (sqlClientPool: Pool) => { sqlClientPool, Sql.selectEnvironmentByNameAndProject( environmentName, - projectId - ) + projectId, + ), ); if (!R.prop(0, rows)) { throw new Error('Unauthorized'); @@ -169,7 +164,7 @@ export const Helpers = (sqlClientPool: Pool) => { const hasName = R.both(R.has('name'), R.propSatisfies(notEmpty, 'name')); const hasProject = R.both( R.has('project'), - R.propSatisfies(notEmpty, 'project') + R.propSatisfies(notEmpty, 'project'), ); // @ts-ignore const hasNameAndProject = R.both(hasName, hasProject); @@ -183,19 +178,19 @@ export const Helpers = (sqlClientPool: Pool) => { } return [environment]; - } + }, ); const envFromNameProject = async input => { const project = await projectHelpers( - sqlClientPool + sqlClientPool, ).getProjectByProjectInput(R.prop('project', input)); const rows = await query( sqlClientPool, Sql.selectEnvironmentByNameAndProject( R.prop('name', input), - project.id - ) + project.id, + ), ); if (!R.prop(0, rows)) { @@ -213,10 +208,10 @@ export const Helpers = (sqlClientPool: Pool) => { R.T, () => { throw new Error( - 'Must provide environment (id) or (name and project)' + 'Must provide environment (id) or (name and project)', ); - } - ] + }, + ], // @ts-ignore ])(environmentInput); }, @@ -224,20 +219,20 @@ export const Helpers = (sqlClientPool: Pool) => { const rows = await query( sqlClientPool, Sql.selectServicesByEnvironmentId( - eid - ) + eid, + ), ); return rows; }, resetServiceContainers: async (serviceId: number, containers: any) => { await query( sqlClientPool, - Sql.deleteServiceContainers(serviceId) + Sql.deleteServiceContainers(serviceId), ); - for (const container of containers){ + for (const container of containers) { await query( sqlClientPool, - Sql.insertServiceContainer(serviceId, container.name) + Sql.insertServiceContainer(serviceId, container.name), ); } }, diff --git a/services/api/src/resources/environment/resolvers.ts b/services/api/src/resources/environment/resolvers.ts index 644a651fc9..b70e6e19cb 100644 --- a/services/api/src/resources/environment/resolvers.ts +++ b/services/api/src/resources/environment/resolvers.ts @@ -1,8 +1,8 @@ import * as R from 'ramda'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; -import { seedNamespace, createMiscTask } from '@lagoon/commons/dist/tasks'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; +import { seedNamespace, createMiscTask } from '../../commons/tasks'; import { getConfigFromEnv } from '../../util/config'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { isPatchEmpty, query, knex } from '../../util/db'; import { convertDateToMYSQLDateFormat } from '../../util/convertDateToMYSQLDateTimeFormat'; import { Helpers } from './helpers'; @@ -13,16 +13,14 @@ import { Helpers as openshiftHelpers } from '../openshift/helpers'; import { Helpers as organizationHelpers } from '../organization/helpers'; import { getFactFilteredEnvironmentIds } from '../fact/resolvers'; import { getUserProjectIdsFromRoleProjectIds } from '../../util/auth'; -import { RemoveData, DeployType, AuditType } from '@lagoon/commons/dist/types'; +import { RemoveData, DeployType, AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; import { logger } from '../../loggers/logger'; // Helper resolver to convert kibUsed to deprecated bytesUsed. export const getBytesUsed: ResolverFn = async ( - envStorage -) => { - return envStorage.kibUsed -} + envStorage, +) => envStorage.kibUsed; interface EnvironmentService { name: string @@ -35,11 +33,10 @@ interface EnvironmentService { export const getEnvironmentByName: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { - - if (args.includeDeleted == undefined) { - args.includeDeleted = true + if (args.includeDeleted === undefined) { + args.includeDeleted = true; } const rows = await query(sqlClientPool, Sql.selectEnvironmentByNameAndProjectWithArgs(args.name, args.project, args.includeDeleted)); @@ -54,7 +51,7 @@ export const getEnvironmentByName: ResolverFn = async ( // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: args.project + project: args.project, }); } @@ -64,12 +61,12 @@ export const getEnvironmentByName: ResolverFn = async ( export const getEnvironmentById = async ( eid, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { // handle dealing with arg or passthrough - let environmentId = args.id + let environmentId = args.id; if (eid) { - environmentId = eid.environment + environmentId = eid.environment; } const environment = await Helpers(sqlClientPool).getEnvironmentById(environmentId); @@ -79,7 +76,7 @@ export const getEnvironmentById = async ( // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: environment.project + project: environment.project, }); } @@ -89,23 +86,25 @@ export const getEnvironmentById = async ( export const getEnvironmentsByProjectId: ResolverFn = async ( project, args, - { sqlClientPool, hasPermission, keycloakGrant, models, adminScopes } + { + sqlClientPool, hasPermission, adminScopes, + }, ) => { const { id: pid } = project; // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: pid + project: pid, }); } let filterEnvironments = false; - let filteredEnvironments = []; + let filteredEnvironments: any[] = []; if (args.factFilter && args.factFilter.filters && args.factFilter.filters.length !== 0) { filterEnvironments = true; - filteredEnvironments = await getFactFilteredEnvironmentIds(args.factFilter, [project.id], sqlClientPool, false); + filteredEnvironments = await getFactFilteredEnvironmentIds(args.factFilter, [project.id], sqlClientPool, false) as any[]; } const rows = await query(sqlClientPool, Sql.selectEnvironmentsByProjectId(args.type, pid, args.includeDeleted, filterEnvironments, filteredEnvironments)); @@ -118,9 +117,9 @@ export const getEnvironmentsByProjectId: ResolverFn = async ( export const getEnvironmentByDeploymentId: ResolverFn = async ( { id: deployment_id }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { - const rows = await query(sqlClientPool, Sql.selectEnvironmentByDeploymentId(deployment_id)) + const rows = await query(sqlClientPool, Sql.selectEnvironmentByDeploymentId(deployment_id)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); const environment = withK8s[0]; @@ -131,7 +130,7 @@ export const getEnvironmentByDeploymentId: ResolverFn = async ( // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: environment.project + project: environment.project, }); } @@ -141,9 +140,9 @@ export const getEnvironmentByDeploymentId: ResolverFn = async ( export const getEnvironmentByTaskId: ResolverFn = async ( { id: task_id }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { - const rows = await query(sqlClientPool, Sql.selectEnvironmentByTaskId(task_id)) + const rows = await query(sqlClientPool, Sql.selectEnvironmentByTaskId(task_id)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); const environment = withK8s[0]; @@ -154,7 +153,7 @@ export const getEnvironmentByTaskId: ResolverFn = async ( // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: environment.project + project: environment.project, }); } @@ -164,9 +163,9 @@ export const getEnvironmentByTaskId: ResolverFn = async ( export const getEnvironmentByBackupId: ResolverFn = async ( { id: backup_id }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { - const rows = await query(sqlClientPool, Sql.selectEnvironmentByBackupId(backup_id)) + const rows = await query(sqlClientPool, Sql.selectEnvironmentByBackupId(backup_id)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); const environment = withK8s[0]; @@ -177,7 +176,7 @@ export const getEnvironmentByBackupId: ResolverFn = async ( // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: environment.project + project: environment.project, }); } @@ -187,7 +186,7 @@ export const getEnvironmentByBackupId: ResolverFn = async ( export const getEnvironmentStorageByEnvironmentId: ResolverFn = async ( { id: eid }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { if (args) { // set lastDays to args.lastDays, or 60 if undefined @@ -210,14 +209,14 @@ export const getEnvironmentStorageByEnvironmentId: ResolverFn = async ( lastDays, claim: args.claim, startDate: args.startDate, - endDate: args.endDate - })) + endDate: args.endDate, + })); return rows; } await hasPermission('environment', 'storage'); - const rows = await query(sqlClientPool, Sql.selectEnvironmentStorageByEnvironmentId(eid)) + const rows = await query(sqlClientPool, Sql.selectEnvironmentStorageByEnvironmentId(eid)); return rows; }; @@ -225,52 +224,51 @@ export const getEnvironmentStorageByEnvironmentId: ResolverFn = async ( export const getEnvironmentStorageMonthByEnvironmentId: ResolverFn = async ( { id: eid }, args, - { models, hasPermission } + { models, hasPermission }, ) => { await hasPermission('environment', 'storage'); return models.EnvironmentModel.environmentStorageMonthByEnvironmentId( eid, - args.month + args.month, ); }; export const getEnvironmentHoursMonthByEnvironmentId: ResolverFn = async ( { id: eid }, args, - { models, hasPermission } + { models, hasPermission }, ) => { await hasPermission('environment', 'storage'); return models.EnvironmentModel.environmentHoursMonthByEnvironmentId( eid, - args.month + args.month, ); }; export const getEnvironmentHitsMonthByEnvironmentId: ResolverFn = async ( { id, openshiftProjectName }, args, - { sqlClientPool, models, hasPermission } + { sqlClientPool, models, hasPermission }, ) => { await hasPermission('environment', 'storage'); const { name: projectName } = await projectHelpers( - sqlClientPool + sqlClientPool, ).getProjectByEnvironmentId(id); return models.EnvironmentModel.environmentHitsMonthByEnvironmentId( projectName, openshiftProjectName, - args.month + args.month, ); }; export const getEnvironmentByOpenshiftProjectName: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { - const rows = await query(sqlClientPool, Sql.selectEnvironmentByOpenshiftProjectName(args.openshiftProjectName)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); @@ -283,7 +281,7 @@ export const getEnvironmentByOpenshiftProjectName: ResolverFn = async ( // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: environment.project + project: environment.project, }); } @@ -293,24 +291,27 @@ export const getEnvironmentByOpenshiftProjectName: ResolverFn = async ( export const getEnvironmentByKubernetesNamespaceName: ResolverFn = async ( root, args, - ctx -) => - getEnvironmentByOpenshiftProjectName( - root, - { - ...args, - openshiftProjectName: args.kubernetesNamespaceName - }, - ctx - ); + ctx, +) => getEnvironmentByOpenshiftProjectName( + root, + { + ...args, + openshiftProjectName: args.kubernetesNamespaceName, + }, + ctx, +); export const getEnvironmentsByKubernetes: ResolverFn = async ( _, - { kubernetes, order, createdAfter, type }, - { sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups, adminScopes } + { + kubernetes, order, createdAfter, type, + }, + { + sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups, adminScopes, + }, ) => { const openshift = await openshiftHelpers( - sqlClientPool + sqlClientPool, ).getOpenshiftByOpenshiftInput(kubernetes); let userProjectIds: number[]; @@ -318,7 +319,7 @@ export const getEnvironmentsByKubernetes: ResolverFn = async ( if (!adminScopes.platformOwner && !adminScopes.platformViewer) { const userProjectRoles = await models.UserModel.getAllProjectsIdsForUser(keycloakGrant.access_token.content.sub, keycloakUsersGroups); userProjectIds = getUserProjectIdsFromRoleProjectIds(userProjectRoles); - if (userProjectIds.length == 0) { + if (userProjectIds.length === 0) { // return an empty result if the user has no project ids return []; } @@ -332,18 +333,18 @@ export const getEnvironmentsByKubernetes: ResolverFn = async ( for (const pid of userProjectIds) { try { await hasPermission('openshift', 'view', { - project: pid + project: pid, }); projectsWithOpenshiftViewPermission = [ ...projectsWithOpenshiftViewPermission, - pid + pid, ]; } catch { } } queryBuilder = queryBuilder.whereIn( 'project', - projectsWithOpenshiftViewPermission + projectsWithOpenshiftViewPermission, ); } @@ -368,13 +369,14 @@ export const getEnvironmentsByKubernetes: ResolverFn = async ( export const addOrUpdateEnvironment: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { - const pid = input.project.toString(); await hasPermission('environment', `addOrUpdate:${input.environmentType}`, { - project: pid + project: pid, }); // get the project information @@ -401,14 +403,14 @@ export const addOrUpdateEnvironment: ResolverFn = async ( try { const curEnv = await Helpers(sqlClientPool).getEnvironmentById(input.id); - openshift = curEnv.openshift + openshift = curEnv.openshift; // set the namespace to whatever the current environment is to ensure it remains unchanged - envNamespaceName = curEnv.openshiftProjectName - } catch (err) { + envNamespaceName = curEnv.openshiftProjectName; + } catch (_err) { // do nothing } if (!openshift) { - openshift = project.openshift + openshift = project.openshift; } // only allow administrators to change the namespace name @@ -422,13 +424,13 @@ export const addOrUpdateEnvironment: ResolverFn = async ( if (project.organization) { // if this would be a new environment, check it against the quota - const curEnvs = await organizationHelpers(sqlClientPool).getEnvironmentsByOrganizationId(project.organization) + const curEnvs = await organizationHelpers(sqlClientPool).getEnvironmentsByOrganizationId(project.organization); if (!curEnvs.map(e => e.name).find(i => i === input.name)) { // check the environment quota, this prevents environments being added directly via the api - const curOrg = await organizationHelpers(sqlClientPool).getOrganizationById(project.organization) - if (curEnvs.length >= curOrg.quotaEnvironment && curOrg.quotaEnvironment != -1) { + const curOrg = await organizationHelpers(sqlClientPool).getOrganizationById(project.organization); + if (curEnvs.length >= curOrg.quotaEnvironment && curOrg.quotaEnvironment !== -1) { throw new Error( - `Environment would exceed organization environment quota: ${curEnvs.length}/${curOrg.quotaEnvironment}` + `Environment would exceed organization environment quota: ${curEnvs.length}/${curOrg.quotaEnvironment}`, ); } } @@ -459,7 +461,7 @@ export const addOrUpdateEnvironment: ResolverFn = async ( 'deployType', 'environmentType', ]), - R.mergeDeepRight({ updated: knex.fn.now() }) + R.mergeDeepRight({ updated: knex.fn.now() }) as any, )(input); const createOrUpdateSql = knex('environment') @@ -471,8 +473,9 @@ export const addOrUpdateEnvironment: ResolverFn = async ( }) .onConflict('id') .merge({ - ...updateData - }).toString(); + ...(updateData as any), + }) + .toString(); const { insertId } = await query( sqlClientPool, @@ -481,7 +484,7 @@ export const addOrUpdateEnvironment: ResolverFn = async ( const rows = await query(sqlClientPool, Sql.selectEnvironmentById(insertId)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s([ - R.path([0], rows) + R.path([0], rows), ]); const environment = withK8s[0]; @@ -500,13 +503,13 @@ export const addOrUpdateEnvironment: ResolverFn = async ( if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User updated environment`, { + userActivityLogger('User updated environment', { project: '', event: 'api:addOrUpdateEnvironment', payload: { ...input, ...auditLog, - } + }, }); return environment; @@ -516,11 +519,11 @@ export const addOrUpdateEnvironment: ResolverFn = async ( export const addOrUpdateEnvironmentStorage: ResolverFn = async ( _, { input }, - context + context, ) => { - input.kibUsed = input.bytesUsed + input.kibUsed = input.bytesUsed; - return addOrUpdateStorageOnEnvironment(undefined, { input: input }, context); + return addOrUpdateStorageOnEnvironment(undefined, { input }, context); }; export const addOrUpdateStorageOnEnvironment: ResolverFn = async ( @@ -607,56 +610,56 @@ export const addOrUpdateStorageOnEnvironment: ResolverFn = async ( export const deleteEnvironment: ResolverFn = async ( root, { input: { project: projectName, name, execute } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const projectId = await projectHelpers(sqlClientPool).getProjectIdByName( - projectName + projectName, ); const projectRows = await query( sqlClientPool, - projectSql.selectProject(projectId) + projectSql.selectProject(projectId), ); const project = projectRows[0]; const environmentRows = await query( sqlClientPool, - Sql.selectEnvironmentByNameAndProject(name, projectId) + Sql.selectEnvironmentByNameAndProject(name, projectId), ); const environment = environmentRows[0]; if (!environment) { throw new Error( - `Environment "${name}" does not exist in project "${projectId}"` + `Environment "${name}" does not exist in project "${projectId}"`, ); } await hasPermission('environment', `delete:${environment.environmentType}`, { - project: projectId + project: projectId, }); let canDeleteProduction; try { await hasPermission('environment', 'delete:production', { - project: projectId + project: projectId, }); canDeleteProduction = true; - } catch (err) { + } catch (_err) { canDeleteProduction = false; } - let removeData: RemoveData = { + const removeData: RemoveData = { projectName: project.name, openshiftProjectName: environment.openshiftProjectName, forceDeleteProductionEnvironment: canDeleteProduction, - environmentName: name + environmentName: name, }; const meta: { [key: string]: any; } = { projectName: removeData.projectName, - environmentName: environment.name + environmentName: environment.name, }; // @TODO: this switch can probably be removed, there shouldn't ever be no deploytype on an environment @@ -672,7 +675,7 @@ export const deleteEnvironment: ResolverFn = async ( '', 'api:deleteEnvironment:error', meta, - `*[${removeData.projectName}]* Unknown deploy type ${environment.deployType} \`${environment.name}\`` + `*[${removeData.projectName}]* Unknown deploy type ${environment.deployType} \`${environment.name}\``, ); return `Error: unknown deploy type ${environment.deployType}`; } @@ -687,12 +690,12 @@ export const deleteEnvironment: ResolverFn = async ( try { // check the permission to delete with noexec, typically platform level only or system call await hasPermission('environment', 'deleteNoExec', { - project: projectId + project: projectId, }); await Helpers(sqlClientPool).deleteEnvironment(name, environment.id, projectId); // mark this env as being deleted deleted = true; - } catch (err) { + } catch (_err) { // Not allowed to stop execution, proceed with the remaining process of trying to delete the environment the usual way } } @@ -700,7 +703,7 @@ export const deleteEnvironment: ResolverFn = async ( // if the deploytarget of this environment is marked as disabled or doesn't exist, just delete the environment // the removetask will never work if the deploytarget is disabled and the environment will remain undeleted in the api const deploytarget = await Helpers(sqlClientPool).getEnvironmentsDeploytarget(environment.openshift); - if (deploytarget.length == 0 || deploytarget[0].disabled) { + if (deploytarget.length === 0 || deploytarget[0].disabled) { // if the deploytarget is disabled, delete the environment await Helpers(sqlClientPool).deleteEnvironment(name, environment.id, projectId); // mark this env as being deleted @@ -728,10 +731,10 @@ export const deleteEnvironment: ResolverFn = async ( payload: { projectName, environment, - deleted: deleted, // log if the actual deletion took place + deleted, // log if the actual deletion took place removeData, ...auditLog, - } + }, }); if (deleted) { @@ -752,7 +755,7 @@ export const deleteEnvironment: ResolverFn = async ( ); if (!response.ok) { const errorText = await response.text(); - logger.error(`Error removing ${environment.name}: ${errorText}`) + logger.error(`Error removing ${environment.name}: ${errorText}`); throw new Error(`Error removing ${environment.name}`); } @@ -762,7 +765,7 @@ export const deleteEnvironment: ResolverFn = async ( '', 'api:deleteEnvironment', meta, - `*[${removeData.projectName}]* Deleting environment \`${environment.name}\`` + `*[${removeData.projectName}]* Deleting environment \`${environment.name}\``, ); return 'success'; @@ -771,7 +774,9 @@ export const deleteEnvironment: ResolverFn = async ( export const updateEnvironment: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { if (isPatchEmpty(input)) { throw new Error('input.patch requires at least 1 attribute'); @@ -781,7 +786,7 @@ export const updateEnvironment: ResolverFn = async ( const curEnv = await Helpers(sqlClientPool).getEnvironmentById(id); await hasPermission('environment', `update:${curEnv.environmentType}`, { - project: curEnv.project + project: curEnv.project, }); if (input.patch.openshiftProjectName || input.patch.kubernetesNamespaceName) { @@ -789,18 +794,17 @@ export const updateEnvironment: ResolverFn = async ( throw new Error('Setting namespace is only available to administrators.'); } } - const openshiftProjectName = - input.patch.kubernetesNamespaceName || input.patch.openshiftProjectName; + const openshiftProjectName = input.patch.kubernetesNamespaceName || input.patch.openshiftProjectName; const newType = R.pathOr( curEnv.environmentType, ['patch', 'environmentType'], - input + input, ); const newProject = R.pathOr(curEnv.project, ['patch', 'project'], input); await hasPermission('environment', `update:${newType}`, { - project: newProject + project: newProject, }); await query( @@ -820,9 +824,9 @@ export const updateEnvironment: ResolverFn = async ( routes: input.patch.routes, autoIdle: input.patch.autoIdle, created: input.patch.created, - idleState: input.patch.idleState - } - }) + idleState: input.patch.idleState, + }, + }), ); const rows = await query(sqlClientPool, Sql.selectEnvironmentById(id)); @@ -834,7 +838,7 @@ export const updateEnvironment: ResolverFn = async ( resource: { id: curEnv.project.toString(), type: AuditType.PROJECT, - details: project.name + details: project.name, }, linkedResource: { id: curEnv.id.toString(), @@ -863,11 +867,11 @@ export const updateEnvironment: ResolverFn = async ( routes: input.patch.routes, autoIdle: input.patch.autoIdle, created: input.patch.created, - idleState: input.patch.idleState + idleState: input.patch.idleState, }, data: withK8s, ...auditLog, - } + }, }); return R.prop(0, withK8s); @@ -876,7 +880,7 @@ export const updateEnvironment: ResolverFn = async ( export const getAllEnvironments: ResolverFn = async ( root, { createdAfter, type, order }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { await hasPermission('environment', 'viewAll'); @@ -903,13 +907,13 @@ export const getAllEnvironments: ResolverFn = async ( export const setEnvironmentServices: ResolverFn = async ( root, { input: { environment: environmentId, services } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await Helpers(sqlClientPool).getEnvironmentById( - environmentId + environmentId, ); await hasPermission('environment', `update:${environment.environmentType}`, { - project: environment.project + project: environment.project, }); await query(sqlClientPool, Sql.deleteServices(environmentId)); @@ -917,12 +921,12 @@ export const setEnvironmentServices: ResolverFn = async ( // remove any duplicates, since there is no other identifying information related to these duplicates don't matter. // as this function is also being deprecated its usage over time will eventually drop // this means removal of duplicates is an acceptable trade off while the transition takes place - var uniq = services.filter((value, index, array) => array.indexOf(value) === index); + const uniq = services.filter((value, index, array) => array.indexOf(value) === index); for (const service of uniq) { await query(sqlClientPool, Sql.insertService(environmentId, service)); } - const project = await projectHelpers(sqlClientPool).getProjectById(environment.project) + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); const auditLog: AuditLog = { resource: { @@ -943,22 +947,21 @@ export const setEnvironmentServices: ResolverFn = async ( environment, services, ...auditLog, - } + }, }); return query( sqlClientPool, - Sql.selectServicesByEnvironmentId(environmentId) + Sql.selectServicesByEnvironmentId(environmentId), ); }; export const userCanSshToEnvironment: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { - const openshiftProjectName = - args.kubernetesNamespaceName || args.openshiftProjectName; + const openshiftProjectName = args.kubernetesNamespaceName || args.openshiftProjectName; const rows = await query(sqlClientPool, Sql.canSshToEnvironment(openshiftProjectName)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); @@ -970,11 +973,11 @@ export const userCanSshToEnvironment: ResolverFn = async ( try { await hasPermission('environment', `ssh:${environment.environmentType}`, { - project: environment.project + project: environment.project, }); return environment; - } catch (err) { + } catch (_err) { return null; } }; @@ -984,16 +987,16 @@ export const userCanSshToEnvironment: ResolverFn = async ( export const addOrUpdateEnvironmentService: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await Helpers(sqlClientPool).getEnvironmentById( - input.environment + input.environment, ); await hasPermission('environment', `update:${environment.environmentType}`, { - project: environment.project + project: environment.project, }); - let envService: EnvironmentService = { + const envService: EnvironmentService = { name: input.name, type: input.type, environment: environment.id, @@ -1002,7 +1005,7 @@ export const addOrUpdateEnvironmentService: ResolverFn = async ( if (input.replicas || input.replicas === 0) { // set the replica count if provided - envService.replicas = input.replicas + envService.replicas = input.replicas; } const createOrUpdateSql = knex('environment_service') @@ -1011,21 +1014,22 @@ export const addOrUpdateEnvironmentService: ResolverFn = async ( }) .onConflict('id') .merge({ - ...envService - }).toString(); + ...envService, + }) + .toString(); const { insertId } = await query( sqlClientPool, createOrUpdateSql); - if (input.containers){ + if (input.containers) { // reset this services containers (delete all and add the current ones if provided) - await Helpers(sqlClientPool).resetServiceContainers(insertId, input.containers) + await Helpers(sqlClientPool).resetServiceContainers(insertId, input.containers); } const rows = await query(sqlClientPool, Sql.selectEnvironmentServiceById(insertId)); - const project = await projectHelpers(sqlClientPool).getProjectById(environment.project) + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); const auditLog: AuditLog = { resource: { @@ -1045,7 +1049,7 @@ export const addOrUpdateEnvironmentService: ResolverFn = async ( payload: { environment, ...auditLog, - } + }, }); // parese the response through the servicecontainer helper @@ -1056,10 +1060,9 @@ export const addOrUpdateEnvironmentService: ResolverFn = async ( export const deleteEnvironmentService: ResolverFn = async ( root, { input: { name, environment: eid } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - - const rows = await query(sqlClientPool, Sql.selectEnvironmentById(eid)) + const rows = await query(sqlClientPool, Sql.selectEnvironmentById(eid)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); const environment = withK8s[0]; @@ -1075,12 +1078,12 @@ export const deleteEnvironmentService: ResolverFn = async ( } await hasPermission('environment', `delete:${environment.environmentType}`, { - project: environment.project + project: environment.project, }); await query(sqlClientPool, Sql.deleteEnvironmentServiceById(service.id)); - const project = await projectHelpers(sqlClientPool).getProjectById(environment.project) + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); const auditLog: AuditLog = { resource: { @@ -1100,7 +1103,7 @@ export const deleteEnvironmentService: ResolverFn = async ( payload: { service, ...auditLog, - } + }, }); return 'success'; @@ -1111,9 +1114,9 @@ export const deleteEnvironmentService: ResolverFn = async ( export const getEnvironmentByServiceId: ResolverFn = async ( { id: service_id }, args, - { sqlClientPool } + { sqlClientPool }, ) => { - const rows = await query(sqlClientPool, Sql.selectEnvironmentByServiceId(service_id)) + const rows = await query(sqlClientPool, Sql.selectEnvironmentByServiceId(service_id)); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); const environment = withK8s[0]; @@ -1129,9 +1132,9 @@ export const getEnvironmentByServiceId: ResolverFn = async ( export const getEnvironmentServicesByEnvironmentId: ResolverFn = async ( { id: eid }, args, - { sqlClientPool } + { sqlClientPool }, ) => { - const rows = await Helpers(sqlClientPool).getEnvironmentServices(eid) + const rows = await Helpers(sqlClientPool).getEnvironmentServices(eid); return rows; }; @@ -1140,11 +1143,11 @@ export const getEnvironmentServicesByEnvironmentId: ResolverFn = async ( export const getServiceContainersByServiceId: ResolverFn = async ( { id: sid }, args, - { sqlClientPool } + { sqlClientPool }, ) => { const rows = await query( sqlClientPool, - Sql.selectContainersByServiceId(sid) + Sql.selectContainersByServiceId(sid), ); return await rows; }; @@ -1156,18 +1159,18 @@ export const idleOrUnidleEnvironment = async ( environment, project, idle, - disableAutomaticUnidling - } + disableAutomaticUnidling, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project) - const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId) - const env = await Helpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - const environmentData = env[0] + const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project); + const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId); + const env = await Helpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + const environmentData = env[0]; if (!environmentData) { throw new Error( - `Unauthorized: You don't have permission to "idle" on "environment_state"` + 'Unauthorized: You don\'t have permission to "idle" on "environment_state"', ); } if (disableAutomaticUnidling) { @@ -1175,8 +1178,8 @@ export const idleOrUnidleEnvironment = async ( 'environment_state', `forceScale:${environmentData.environmentType}`, { - project: projectId - } + project: projectId, + }, ); } if (idle) { @@ -1186,8 +1189,8 @@ export const idleOrUnidleEnvironment = async ( 'environment_state', `idle:${environmentData.environmentType}`, { - project: projectId - } + project: projectId, + }, ); break; default: @@ -1195,35 +1198,35 @@ export const idleOrUnidleEnvironment = async ( 'environment_state', `unidle:${environmentData.environmentType}`, { - project: projectId - } + project: projectId, + }, ); break; } } - if (projectData.autoIdle == 0) { + if (projectData.autoIdle === 0) { // if the project has idling disabled, then don't allow idling or unidling of the environment throw new Error( - `Project has idling disabled` + 'Project has idling disabled', ); - } else if (projectData.autoIdle == 1 && environmentData.autoIdle == 0) { + } else if (projectData.autoIdle === 1 && environmentData.autoIdle === 0) { // if the project has idling enabled, but the environment has it disabled // then don't allow idling or unidling of the environment throw new Error( - `Environment has idling disabled` + 'Environment has idling disabled', ); } // don't try idle if the environment is already idled or unidled - if (environmentData.idleState != 'active' && idle) { + if (environmentData.idleState !== 'active' && idle) { throw new Error( - `Environment is already idled` + 'Environment is already idled', ); } - if (environmentData.idleState == 'active' && !idle) { + if (environmentData.idleState === 'active' && !idle) { throw new Error( - `Environment is already unidled` + 'Environment is already unidled', ); } @@ -1231,9 +1234,9 @@ export const idleOrUnidleEnvironment = async ( environment: environmentData, project: projectData, idling: { - idle: idle, - forceScale: disableAutomaticUnidling - } + idle, + forceScale: disableAutomaticUnidling, + }, }; const auditLog: AuditLog = { @@ -1258,7 +1261,7 @@ export const idleOrUnidleEnvironment = async ( project: projectData.name, environment: environmentData.name, ...auditLog, - } + }, }); try { @@ -1271,10 +1274,10 @@ export const idleOrUnidleEnvironment = async ( '', 'api:idleOrUnidleEnvironment', { environment: environmentData.id }, - `Environment idle attempt possibly failed, reason: ${error}` + `Environment idle attempt possibly failed, reason: ${error}`, ); throw new Error( - error.message + error.message, ); } }; @@ -1286,64 +1289,64 @@ export const stopOrStartEnvironmentService = async ( environment, project, serviceName, - state - } + state, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project) - const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId) - const env = await Helpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - const environmentData = env[0] + const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project); + const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId); + const env = await Helpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + const environmentData = env[0]; if (!environmentData) { throw new Error( - `Unauthorized: You don't have permission to "restart" on "environment_state"` + 'Unauthorized: You don\'t have permission to "restart" on "environment_state"', ); } - if (environmentData.idleState != 'active') { + if (environmentData.idleState !== 'active') { throw new Error( - `Can't perform action because the environment is idled` + 'Can\'t perform action because the environment is idled', ); } await hasPermission( 'environment_state', `restart:${environmentData.environmentType}`, { - project: projectId - } + project: projectId, + }, ); - const rows = await Helpers(sqlClientPool).getEnvironmentServices(environmentData.id) + const rows = await Helpers(sqlClientPool).getEnvironmentServices(environmentData.id); let serviceExists = false; if (rows) { for (const service of rows) { - if (service.name != serviceName) { + if (service.name !== serviceName) { continue; } switch (state) { case 'stop': - if (service.replicas == 0) { + if (service.replicas === 0) { throw new Error( - `Service ${serviceName} is already stopped` + `Service ${serviceName} is already stopped`, ); } break; case 'start': if (service.replicas > 0) { throw new Error( - `Service ${serviceName} is already running` + `Service ${serviceName} is already running`, ); } break; default: break; } - serviceExists = true + serviceExists = true; } } if (!serviceExists) { throw new Error( - `Service ${serviceName} doesn't exist on environment` + `Service ${serviceName} doesn't exist on environment`, ); } @@ -1352,8 +1355,8 @@ export const stopOrStartEnvironmentService = async ( project: projectData, lagoonService: { name: serviceName, - state: state - } + state, + }, }; const auditLog: AuditLog = { @@ -1378,8 +1381,8 @@ export const stopOrStartEnvironmentService = async ( payload: { project: projectData.name, environment: environmentData.name, - ...auditLog - } + ...auditLog, + }, }); try { @@ -1392,10 +1395,10 @@ export const stopOrStartEnvironmentService = async ( '', 'api:stopOrStartEnvironmentService', { environment: environmentData.id }, - `Service state change attempt possibly failed, reason: ${error}` + `Service state change attempt possibly failed, reason: ${error}`, ); throw new Error( - error.message + error.message, ); } }; diff --git a/services/api/src/resources/environment/sql.ts b/services/api/src/resources/environment/sql.ts index e84d891b33..fb9ab6cd5d 100644 --- a/services/api/src/resources/environment/sql.ts +++ b/services/api/src/resources/environment/sql.ts @@ -12,20 +12,18 @@ export const Sql = { .update(updatePatch) .toString(); }, - selectEnvironmentById: (id: number) => - knex('environment') - .where('id', '=', id) - .toString(), - selectEnvironmentByNameAndProject: (name: string, projectId: number) => - knex('environment') - .where('name', '=', name) - .andWhere('project', '=', projectId) - .andWhere('deleted', '0000-00-00 00:00:00') - .toString(), + selectEnvironmentById: (id: number) => knex('environment') + .where('id', '=', id) + .toString(), + selectEnvironmentByNameAndProject: (name: string, projectId: number) => knex('environment') + .where('name', '=', name) + .andWhere('project', '=', projectId) + .andWhere('deleted', '0000-00-00 00:00:00') + .toString(), selectEnvironmentByNameAndProjectWithArgs: (name: string, projectId: number, includeDeleted: boolean) => { const query = knex('environment') .where('name', '=', name) - .andWhere('project', '=', projectId) + .andWhere('project', '=', projectId); if (!includeDeleted) { return query.andWhere('deleted', '0000-00-00 00:00:00').toString(); @@ -35,15 +33,15 @@ export const Sql = { }, selectEnvironmentsByProjectId: (type: string, projectId: number, includeDeleted: boolean, filterEnvironments: boolean, filteredEnvironments: string[]) => { let query = knex('environment') - .where(knex.raw('project = ?', projectId)) + .where(knex.raw('project = ?', projectId)); if (!includeDeleted) { - query = query.andWhere('deleted', '0000-00-00 00:00:00') + query = query.andWhere('deleted', '0000-00-00 00:00:00'); } if (type) { - query = query.andWhere(knex.raw('environment_type = ?', type)) + query = query.andWhere(knex.raw('environment_type = ?', type)); } if (filterEnvironments && filteredEnvironments.length !== 0) { - query = query.andWhere('id', 'in', filteredEnvironments.join(",")) + query = query.andWhere('id', 'in', filteredEnvironments.join(',')); } return query.toString(); @@ -58,82 +56,71 @@ export const Sql = { select = select.andWhere('deleted', '0000-00-00 00:00:00'); } - return select.toString() + return select.toString(); }, - truncateEnvironment: () => - knex('environment') - .truncate() - .toString(), + truncateEnvironment: () => knex('environment') + .truncate() + .toString(), insertService: ( environment: number, name: string, - ) => - knex('environment_service') - .insert({ - environment, - name, - }) - .toString(), - selectDeployTarget: (id: number) => - knex('openshift') - .where('id', '=', id) - .toString(), - selectServicesByEnvironmentId: (id: number) => - knex('environment_service') - .where('environment', '=', id) - .toString(), - deleteEnvironmentVariables: (id: number) => - knex('env_vars') - .where('environment', '=', id) - .delete() - .toString(), - deleteEnvironment: (name: string, projectId: number) => - knex('environment') - .where('name', name) - .andWhere('project', projectId) - .andWhere('deleted', '0000-00-00 00:00:00') - .update({ deleted: knex.fn.now() }).toString(), - deleteServices: (id: number) => - knex('environment_service') - .where('environment', '=', id) - .delete() - .toString(), - selectEnvironmentByDeploymentId: (id: number) => - knex('deployment') - .select('e.*') - .join('environment AS e', 'deployment.environment', '=', 'e.id') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('deployment.id = ?', id)) - .limit(1) - .toString(), - selectEnvironmentByTaskId: (id: number) => - knex('task') - .select('e.*') - .join('environment AS e', 'task.environment', '=', 'e.id') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('task.id = ?', id)) - .limit(1) - .toString(), - selectEnvironmentByBackupId: (id: number) => - knex('environment_backup') - .select('e.*') - .join('environment AS e', 'environment_backup.environment', '=', 'e.id') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('environment_backup.id = ?', id)) - .limit(1) - .toString(), - selectEnvironmentBySnapshotId: (id: number) => - knex('environment_backup') - .select('e.*') - .join('environment AS e', 'environment_backup.environment', '=', 'e.id') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('environment_backup.backup_id = ?', id)) - .limit(1) - .toString(), - selectEnvironmentStorageByEnvironmentId: (id: number) => - knex('environment_storage') - .where(knex.raw('environment_storage.environment = ?', id)) - .toString(), + ) => knex('environment_service') + .insert({ + environment, + name, + }) + .toString(), + selectDeployTarget: (id: number) => knex('openshift') + .where('id', '=', id) + .toString(), + selectServicesByEnvironmentId: (id: number) => knex('environment_service') + .where('environment', '=', id) + .toString(), + deleteEnvironmentVariables: (id: number) => knex('env_vars') + .where('environment', '=', id) + .delete() + .toString(), + deleteEnvironment: (name: string, projectId: number) => knex('environment') + .where('name', name) + .andWhere('project', projectId) + .andWhere('deleted', '0000-00-00 00:00:00') + .update({ deleted: knex.fn.now() }) + .toString(), + deleteServices: (id: number) => knex('environment_service') + .where('environment', '=', id) + .delete() + .toString(), + selectEnvironmentByDeploymentId: (id: number) => knex('deployment') + .select('e.*') + .join('environment AS e', 'deployment.environment', '=', 'e.id') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('deployment.id = ?', id)) + .limit(1) + .toString(), + selectEnvironmentByTaskId: (id: number) => knex('task') + .select('e.*') + .join('environment AS e', 'task.environment', '=', 'e.id') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('task.id = ?', id)) + .limit(1) + .toString(), + selectEnvironmentByBackupId: (id: number) => knex('environment_backup') + .select('e.*') + .join('environment AS e', 'environment_backup.environment', '=', 'e.id') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('environment_backup.id = ?', id)) + .limit(1) + .toString(), + selectEnvironmentBySnapshotId: (id: number) => knex('environment_backup') + .select('e.*') + .join('environment AS e', 'environment_backup.environment', '=', 'e.id') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('environment_backup.backup_id = ?', id)) + .limit(1) + .toString(), + selectEnvironmentStorageByEnvironmentId: (id: number) => knex('environment_storage') + .where(knex.raw('environment_storage.environment = ?', id)) + .toString(), selectEnvironmentStorageByEnvironmentIdByDaysClaim: ({ eid, lastDays, @@ -146,17 +133,16 @@ export const Sql = { startDate?: Date; endDate?: Date; claim?: string; - }) => - knex - .select('id', 'environment', 'persistent_storage_claim', 'kib_used', 'updated') - .from(function () { - this.select( - '*', - knex.raw('ROW_NUMBER() OVER (PARTITION BY persistent_storage_claim ORDER BY updated ASC) as rn') - ) + }) => knex + .select('id', 'environment', 'persistent_storage_claim', 'kib_used', 'updated') + .from(function () { + this.select( + '*', + knex.raw('ROW_NUMBER() OVER (PARTITION BY persistent_storage_claim ORDER BY updated ASC) as rn'), + ) .from('environment_storage') .where('environment', eid) - .modify(function (queryBuilder) { + .modify((queryBuilder) => { if (claim) { queryBuilder.where('persistent_storage_claim', claim); } @@ -173,88 +159,75 @@ export const Sql = { } }) .as('subquery'); - }) - .toString(), - selectEnvironmentByOpenshiftProjectName: (openshiftProjectName: string) => - knex('environment AS e') - .select('e.*') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('e.openshift_project_name = ?', openshiftProjectName)) - .andWhere('e.deleted', '0000-00-00 00:00:00') - .toString(), - canSshToEnvironment: (openshiftProjectName: string) => - knex('environment AS e') - .select('e.*') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('e.openshift_project_name = ?', openshiftProjectName)) - .toString(), - selectEnvironmentServiceById: (id: number) => - knex('environment_service') - .where('id', '=', id) - .toString(), - selectEnvironmentServiceByName: (name: string, eid: number) => - knex('environment_service') - .where('environment', '=', eid) - .andWhere('name', '=', name) - .toString(), - deleteEnvironmentServiceById: (id: number) => - knex('environment_service') - .where('id', id) - .delete() - .toString(), - selectEnvironmentByServiceId: (id: number) => - knex('environment_service') - .select('e.*') - .join('environment AS e', 'environment_service.environment', '=', 'e.id') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('environment_service.id = ?', id)) - .limit(1) - .toString(), + }) + .toString(), + selectEnvironmentByOpenshiftProjectName: (openshiftProjectName: string) => knex('environment AS e') + .select('e.*') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('e.openshift_project_name = ?', openshiftProjectName)) + .andWhere('e.deleted', '0000-00-00 00:00:00') + .toString(), + canSshToEnvironment: (openshiftProjectName: string) => knex('environment AS e') + .select('e.*') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('e.openshift_project_name = ?', openshiftProjectName)) + .toString(), + selectEnvironmentServiceById: (id: number) => knex('environment_service') + .where('id', '=', id) + .toString(), + selectEnvironmentServiceByName: (name: string, eid: number) => knex('environment_service') + .where('environment', '=', eid) + .andWhere('name', '=', name) + .toString(), + deleteEnvironmentServiceById: (id: number) => knex('environment_service') + .where('id', id) + .delete() + .toString(), + selectEnvironmentByServiceId: (id: number) => knex('environment_service') + .select('e.*') + .join('environment AS e', 'environment_service.environment', '=', 'e.id') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('environment_service.id = ?', id)) + .limit(1) + .toString(), // sekect all the containers for a particular service - selectContainersByServiceId: (id: number) => - knex('environment_service_container') - .where('service_id', '=', id) - .toString(), + selectContainersByServiceId: (id: number) => knex('environment_service_container') + .where('service_id', '=', id) + .toString(), // deletes all containers associated to a service - deleteServiceContainers: (id: number) => - knex('environment_service_container') - .where('service_id', '=', id) - .delete() - .toString(), - deleteServiceContainersByEnvironmentId: (eid: number) => - knex('environment_service_container') - .whereIn('service_id', function() { - this.select('id') - .from('environment_service') - .where('environment', eid); - }) - .delete() - .toString(), + deleteServiceContainers: (id: number) => knex('environment_service_container') + .where('service_id', '=', id) + .delete() + .toString(), + deleteServiceContainersByEnvironmentId: (eid: number) => knex('environment_service_container') + .whereIn('service_id', function () { + this.select('id') + .from('environment_service') + .where('environment', eid); + }) + .delete() + .toString(), // add a new service container insertServiceContainer: ( serviceId: number, name: string, - ) => - knex('environment_service_container') - .insert({ - serviceId, - name, - }) - .toString(), + ) => knex('environment_service_container') + .insert({ + serviceId, + name, + }) + .toString(), // delete all environments from environment table that match project id - deleteEnvironmentsByProjectID: (projectId: number) => - knex('environment') - .where('project', '=', projectId) - .delete() - .toString(), - deleteEnvironmentStorageByEnvironmentId: (id: number) => - knex('environment_storage') - .where('environment', '=', id) - .delete() - .toString(), - deleteEnvironmentStorageByEnvironmentIds: (ids: number[]) => - knex('environment_storage') - .whereIn('environment', ids) - .delete() - .toString(), + deleteEnvironmentsByProjectID: (projectId: number) => knex('environment') + .where('project', '=', projectId) + .delete() + .toString(), + deleteEnvironmentStorageByEnvironmentId: (id: number) => knex('environment_storage') + .where('environment', '=', id) + .delete() + .toString(), + deleteEnvironmentStorageByEnvironmentIds: (ids: number[]) => knex('environment_storage') + .whereIn('environment', ids) + .delete() + .toString(), }; diff --git a/services/api/src/resources/environment/validators.ts b/services/api/src/resources/environment/validators.ts index 3bffd42f28..51ae2874c2 100644 --- a/services/api/src/resources/environment/validators.ts +++ b/services/api/src/resources/environment/validators.ts @@ -7,19 +7,19 @@ export const Validators = (sqlClientPool: Pool) => ({ environmentExists: async (environmentId: number) => { const env = await query( sqlClientPool, - Sql.selectEnvironmentById(environmentId) + Sql.selectEnvironmentById(environmentId), ); - if (R.length(env) == 0) { + if (R.length(env) === 0) { // environment doesn't exist, throw an unauthorized error - throw new Error(`Unauthorized: You don't have permission to "view" on "environment"`); + throw new Error('Unauthorized: You don\'t have permission to "view" on "environment"'); } }, environmentsHaveSameProject: async (environmentIds: number[]) => { const rows = await query( sqlClientPool, 'SELECT DISTINCT project FROM environment WHERE id in (?)', - [environmentIds] + [environmentIds], ); const projectIds = R.pluck('project', rows); @@ -27,20 +27,20 @@ export const Validators = (sqlClientPool: Pool) => ({ if (R.length(R.uniq(projectIds)) > 1) { throw new Error( `Environments ${environmentIds.join( - ',' - )} do not belong to the same project.` + ',', + )} do not belong to the same project.`, ); } }, environmentHasService: async (environmentId: number, service: string) => { const rows = await query( sqlClientPool, - Sql.selectServicesByEnvironmentId(environmentId) + Sql.selectServicesByEnvironmentId(environmentId), ); // @ts-ignore if (!R.contains(service, R.pluck('name', rows))) { throw new Error(`Environment ${environmentId} has no service ${service}`); } - } + }, }); diff --git a/services/api/src/resources/fact/resolvers.ts b/services/api/src/resources/fact/resolvers.ts index 6848714530..c406779546 100644 --- a/services/api/src/resources/fact/resolvers.ts +++ b/services/api/src/resources/fact/resolvers.ts @@ -1,88 +1,83 @@ import * as R from 'ramda'; -import { query } from '../../util/db'; +import crypto from 'crypto'; +import { query, knex } from '../../util/db'; import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; import { Sql } from './sql'; import { ResolverFn } from '../index'; -import { knex } from '../../util/db'; import { logger } from '../../loggers/logger'; -import crypto from 'crypto'; import { getUserProjectIdsFromRoleProjectIds } from '../../util/auth'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; export const getFactsByEnvironmentId: ResolverFn = async ( { id: environmentId }, { keyFacts, limit, summary }, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('fact', 'view', { - project: environment.project + project: environment.project, }); } - - var rows = []; + let rows = []; // If we're summarizing facts, we actually can't pass back fact ids, or limit - if(summary) { + if (summary) { rows = await query( sqlClientPool, Sql.selectFactsByEnvironmentId({ environmentId, keyFacts, - limit: false - }) + limit: false, + }), ); rows = summarizeFacts(rows); return R.sort(R.ascend(R.prop('name')), rows); - - } else { - rows = await query( - sqlClientPool, - Sql.selectFactsByEnvironmentId({ - environmentId, - keyFacts, - limit - }) - ); - - return R.sort(R.descend(R.prop('created')), rows); } + rows = await query( + sqlClientPool, + Sql.selectFactsByEnvironmentId({ + environmentId, + keyFacts, + limit, + }), + ); + return R.sort(R.descend(R.prop('created')), rows); }; const summarizeFacts = (rows) => { - const factSummary = new Map(); + const factSummary = new Map(); rows.forEach(element => { - var summaryKey = crypto.createHash('md5') - .update(element['name']) - .update(element['value']) - .update(element['description']) - .digest('hex'); + const summaryKey = crypto.createHash('md5') + .update(element.name) + .update(element.value) + .update(element.description) + .digest('hex'); - //clear identifying marks ... - element['id'] = null; - element['created'] = null; + // clear identifying marks ... + element.id = null; + element.created = null; const summaryConcat = (head, tail) => { - if(head.length > 0) { - return `${head}, ${tail}` + if (head.length > 0) { + return `${head}, ${tail}`; } return tail; - } + }; - if(factSummary.has(summaryKey)) { - var f = factSummary.get(summaryKey); - f['source'] = summaryConcat(f['source'], element['source']); - //TODO : after adding service, we need to add csv of the service names here ... + if (factSummary.has(summaryKey)) { + const f = factSummary.get(summaryKey); + f.source = summaryConcat(f.source, element.source); + // TODO : after adding service, we need to add csv of the service names here ... // f['service'] = summaryConcat(f['service'], element['service']); factSummary.set(summaryKey, f); } else { @@ -92,42 +87,42 @@ const summarizeFacts = (rows) => { return Array.from(factSummary.values()); }; - export const getFactReferencesByFactId: ResolverFn = async ( { id: fid }, args, - { sqlClientPool } + { sqlClientPool }, ) => { const rows = await query( sqlClientPool, - Sql.selectFactReferencesByFactId(fid) + Sql.selectFactReferencesByFactId(fid), ); - return R.sort(R.descend(R.prop('name')), rows); + return R.sort(R.descend((r: any) => r.name), rows); }; -const predicateRHSProcess = (predicate, targetValue) => predicate == 'CONTAINS' ? `%${targetValue}%` : targetValue +const predicateRHSProcess = (predicate, targetValue) => predicate === 'CONTAINS' ? `%${targetValue}%` : targetValue; -const getSqlPredicate = (predicate) => { +const _getSqlPredicate = (predicate) => { const predicateMap = { - 'CONTAINS': 'like', - 'LESS_THAN': '<', - 'LESS_THAN_OR_EQUALS': '<=', - 'GREATER_THAN': '>', - 'GREATER_THAN_OR_EQUALS': '<=', - 'EQUALS': '=', + CONTAINS: 'like', + LESS_THAN: '<', + LESS_THAN_OR_EQUALS: '<=', + GREATER_THAN: '>', + GREATER_THAN_OR_EQUALS: '<=', + EQUALS: '=', }; return predicateMap[predicate]; -} +}; export const getProjectsByFactSearch: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, keycloakGrant, models, keycloakUsersGroups, adminScopes }, - info + { + sqlClientPool, keycloakGrant, models, keycloakUsersGroups, adminScopes, + }, + _info, ) => { - let userProjectIds: number[]; // if the user is not a platform owner or viewer, then perform normal permission check @@ -140,14 +135,15 @@ export const getProjectsByFactSearch: ResolverFn = async ( const rows = await getFactFilteredProjects(input, userProjectIds, sqlClientPool, adminScopes); return { projects: rows, count }; -} +}; export const getEnvironmentsByFactSearch: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, keycloakGrant, models, keycloakUsersGroups, adminScopes } + { + sqlClientPool, keycloakGrant, models, keycloakUsersGroups, adminScopes, + }, ) => { - let userProjectIds: number[]; // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { @@ -159,16 +155,16 @@ export const getEnvironmentsByFactSearch: ResolverFn = async ( const rows = await getFactFilteredEnvironments(input, userProjectIds, sqlClientPool, adminScopes); return { environments: rows, count }; -} +}; export const processAddFacts = async (facts, sqlClientPool, hasPermission, adminScopes) => { const environments = facts.reduce((environmentList, fact) => { - if (fact.environment == undefined) { + if (fact.environment === undefined) { logger.error(`No environment ID given for fact: ${fact.name}`); throw new Error(`No environment ID given for fact: ${fact.name}`); } - let { environment } = fact; + const { environment } = fact; if (!environmentList.includes(environment)) { environmentList.push(environment); } @@ -178,32 +174,34 @@ export const processAddFacts = async (facts, sqlClientPool, hasPermission, admin // admin bypass to skip heavy haspermission checks // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { - let projectIds = [] + let projectIds = []; for (let i = 0; i < environments.length; i++) { const env = await environmentHelpers(sqlClientPool).getEnvironmentById( - environments[i] + environments[i], ); // collect the project ids - projectIds.push(env.project) - }; + projectIds.push(env.project); + } // unique the project ids for more efficient permission checks against projects projectIds = [...new Set(projectIds)]; for (const pid in projectIds) { await hasPermission('fact', 'add', { - project: projectIds[pid] + project: projectIds[pid], }); } } const returnFacts = []; for (let i = 0; i < facts.length; i++) { - const { environment, name, value, source, description, type, category, keyFact, service } = facts[i]; + const { + environment, name, value, source, description, type, category, keyFact, service, + } = facts[i]; let insertId: number; try { - ({insertId} = await query( + ({ insertId } = await query( sqlClientPool, Sql.insertFact({ environment, @@ -214,46 +212,50 @@ export const processAddFacts = async (facts, sqlClientPool, hasPermission, admin type, keyFact, category, - service - }) + service, + }), )); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding fact. Fact already exists.` + 'Error adding fact. Fact already exists.', ); } else { throw new Error(error.message); } - }; + } const rows = await query(sqlClientPool, Sql.selectFactByDatabaseId(insertId)); returnFacts.push(R.prop(0, rows)); } return returnFacts; -} +}; export const addFact: ResolverFn = async ( root, - { input: { id, environment: environmentId, name, value, source, description, type, category, keyFact, service } }, - { sqlClientPool, hasPermission, userActivityLogger } + { + input: { + _id, environment: environmentId, name, value, source, description, type, category, keyFact, service, + }, + }, + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - if (environmentId == undefined) { - logger.error(`No environment ID given for fact: ${name}`) - throw new Error(`No environment ID given for fact: ${name}`) + if (environmentId === undefined) { + logger.error(`No environment ID given for fact: ${name}`); + throw new Error(`No environment ID given for fact: ${name}`); } const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); await hasPermission('fact', 'add', { - project: environment.project + project: environment.project, }); let insertId: number; try { - ({insertId} = await query( + ({ insertId } = await query( sqlClientPool, Sql.insertFact({ environment: environmentId, @@ -264,25 +266,25 @@ export const addFact: ResolverFn = async ( type, keyFact, category, - service + service, }), )); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding fact. Fact already exists.` + 'Error adding fact. Fact already exists.', ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectFactByDatabaseId(insertId) + Sql.selectFactByDatabaseId(insertId), ); - let project = await projectHelpers(sqlClientPool).getProjectById(environment.project); + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); const auditLog: AuditLog = { resource: { @@ -309,10 +311,10 @@ export const addFact: ResolverFn = async ( value, source, description, - service + service, }, ...auditLog, - } + }, }); return R.prop(0, rows); @@ -321,7 +323,9 @@ export const addFact: ResolverFn = async ( export const addFacts: ResolverFn = async ( root, { input: { facts } }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { const returnFacts = await processAddFacts(facts, sqlClientPool, hasPermission, adminScopes); @@ -345,12 +349,12 @@ export const addFacts: ResolverFn = async ( if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User added facts to environment'`, { + userActivityLogger('User added facts to environment\'', { project: '', event: 'api:addFacts', payload: { ...auditLog, - } + }, }); } @@ -360,43 +364,42 @@ export const addFacts: ResolverFn = async ( export const addFactsByName: ResolverFn = async ( root, { input: { project, environment, facts } }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, models, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { - if (!project || !environment) { - throw new Error("Both 'project' and 'environment' require values"); //Presumably this'll be taken care of via the schema, but let's check either way. + throw new Error("Both 'project' and 'environment' require values"); // Presumably this'll be taken care of via the schema, but let's check either way. } - let lagoonProject = await projectHelpers(sqlClientPool).getProjectIdByName(project); + const lagoonProject = await projectHelpers(sqlClientPool).getProjectIdByName(project); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: lagoonProject + project: lagoonProject, }); } - let environments = await environmentHelpers(sqlClientPool).getEnvironmentsByProjectId(lagoonProject); + const environments = await environmentHelpers(sqlClientPool).getEnvironmentsByProjectId(lagoonProject); - if (environments.length == 0) { + if (environments.length === 0) { throw new Error(`No environments found for project '${project}'`); } - let envId = R.reduce((acc, e) => { - return e.name === environment ? e.id : acc; - }, null, environments); + const envId = R.reduce((acc, e) => e.name === environment ? e.id : acc, null, environments); if (!envId) { throw new Error(`No environment '${environment}' found for project '${project}'`); } const returnFacts = await processAddFacts( - R.map((fact:object) => {return {environment: envId, ...fact};}, facts), + R.map((fact:object) => ({ environment: envId, ...fact }), facts), sqlClientPool, hasPermission, adminScopes, ); - let projectData = await projectHelpers(sqlClientPool).getProjectById(lagoonProject); - let environmentData = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, lagoonProject); + const projectData = await projectHelpers(sqlClientPool).getProjectById(lagoonProject); + const environmentData = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, lagoonProject); const auditLog: AuditLog = { resource: { @@ -414,15 +417,15 @@ export const addFactsByName: ResolverFn = async ( auditLog.organizationId = projectData.organization; } userActivityLogger(`User added facts to '${project}:${environment}'`, { - project: project, - environment: environment, + project, + environment, event: 'api:addFactsByName', payload: { data: { - returnFacts + returnFacts, }, ...auditLog, - } + }, }); return returnFacts; @@ -431,17 +434,17 @@ export const addFactsByName: ResolverFn = async ( export const deleteFact: ResolverFn = async ( root, { input: { environment: environmentId, name } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); await hasPermission('fact', 'delete', { - project: environment.project + project: environment.project, }); - let project = await projectHelpers(sqlClientPool).getProjectById(environment.project); + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); await query(sqlClientPool, Sql.deleteFact(environmentId, name)); @@ -460,16 +463,16 @@ export const deleteFact: ResolverFn = async ( if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User deleted a fact`, { + userActivityLogger('User deleted a fact', { project: '', event: 'api:deleteFact', payload: { data: { environment: environmentId, - name + name, }, ...auditLog, - } + }, }); return 'success'; @@ -478,21 +481,21 @@ export const deleteFact: ResolverFn = async ( export const deleteFactsFromSource: ResolverFn = async ( root, { input: { environment: environmentId, source, service } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); - if(environment == null) { + if (environment == null) { throw new Error(`Unable to find environment with id: ${environmentId}`); } await hasPermission('fact', 'delete', { - project: environment.project + project: environment.project, }); - let project = await projectHelpers(sqlClientPool).getProjectById(environment.project); + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); await query(sqlClientPool, Sql.deleteFactsFromSource(environmentId, source, service)); @@ -511,17 +514,17 @@ export const deleteFactsFromSource: ResolverFn = async ( if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User deleted facts`, { + userActivityLogger('User deleted facts', { project: '', event: 'api:deleteFactsFromSource', payload: { data: { environment: environmentId, source, - service + service, }, ...auditLog, - } + }, }); return 'success'; @@ -530,7 +533,7 @@ export const deleteFactsFromSource: ResolverFn = async ( export const addFactReference: ResolverFn = async ( root, { input: { fid, name } }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const fact = await query(sqlClientPool, Sql.selectFactByDatabaseId(fid)); @@ -540,31 +543,31 @@ export const addFactReference: ResolverFn = async ( const environment = await environmentHelpers(sqlClientPool).getEnvironmentById((R.prop(0, fact)).environment); await hasPermission('fact', 'add', { - project: environment.project + project: environment.project, }); let insertId: number; try { - ({insertId} = await query( + ({ insertId } = await query( sqlClientPool, Sql.insertFactReference({ fid, - name - }) + name, + }), )); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding fact reference. Fact reference already exists.` + 'Error adding fact reference. Fact reference already exists.', ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectFactReferenceByDatabaseId(insertId) + Sql.selectFactReferenceByDatabaseId(insertId), ); return R.prop(0, rows); @@ -573,15 +576,14 @@ export const addFactReference: ResolverFn = async ( export const deleteFactReference: ResolverFn = async ( root, { input: { factName, referenceName, eid } }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { - const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(eid); await hasPermission('fact', 'delete', { - project: environment.project + project: environment.project, }); await query( @@ -594,8 +596,8 @@ export const deleteFactReference: ResolverFn = async ( { fName: factName, rName: referenceName, - eid - } + eid, + }, ); return 'success'; @@ -604,19 +606,19 @@ export const deleteFactReference: ResolverFn = async ( export const deleteAllFactReferencesByFactId: ResolverFn = async ( root, { input: { fid } }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const fact = await query( sqlClientPool, - Sql.selectFactByDatabaseId(fid) + Sql.selectFactByDatabaseId(fid), ); const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(R.prop(0, fact).environment); await hasPermission('fact', 'delete', { - project: environment.project + project: environment.project, }); const { affectedRows } = await query(sqlClientPool, Sql.deleteFactReferencesByFactId(fid)); @@ -626,10 +628,7 @@ export const deleteAllFactReferencesByFactId: ResolverFn = async ( return `Success: ${affectedRows} fact reference/s deleted`; }; - -export const getFactFilteredEnvironmentIds = async (filterDetails: any, projectIdSubset: number[], sqlClientPool, adminScopes) => { - return R.map(p => R.prop("id", p), await getFactFilteredEnvironments(filterDetails, projectIdSubset, sqlClientPool, adminScopes)); -}; +export const getFactFilteredEnvironmentIds = async (filterDetails: any, projectIdSubset: number[], sqlClientPool, adminScopes) => R.map(p => R.prop('id', p), await getFactFilteredEnvironments(filterDetails, projectIdSubset, sqlClientPool, adminScopes)); const getFactFilteredProjects = async (filterDetails: any, projectIdSubset: number[], sqlClientPool, adminScopes: any) => { let factQuery = knex('project').distinct('project.*').innerJoin('environment', 'environment.project', 'project.id'); @@ -639,16 +638,15 @@ const getFactFilteredProjects = async (filterDetails: any, projectIdSubset: numb const rows = await query(sqlClientPool, factQuery.toString()); return rows; -} +}; const getFactFilteredProjectsCount = async (filterDetails: any, projectIdSubset: number[], sqlClientPool, adminScopes: any) => { - let factQuery = knex('project').countDistinct({ count: 'project.id'}).innerJoin('environment', 'environment.project', 'project.id'); + let factQuery = knex('project').countDistinct({ count: 'project.id' }).innerJoin('environment', 'environment.project', 'project.id'); factQuery = buildConditionsForFactSearchQuery(filterDetails, factQuery, projectIdSubset, adminScopes); const rows = await query(sqlClientPool, factQuery.toString()); return rows[0].count; -} - +}; const getFactFilteredEnvironments = async (filterDetails: any, projectIdSubset: number[], sqlClientPool, adminScopes: any) => { let factQuery = knex('environment').distinct('environment.*').innerJoin('project', 'environment.project', 'project.id'); @@ -658,47 +656,44 @@ const getFactFilteredEnvironments = async (filterDetails: any, projectIdSubset: const rows = await query(sqlClientPool, factQuery.toString()); return rows; -} +}; const getFactFilteredEnvironmentsCount = async (filterDetails: any, projectIdSubset: number[], sqlClientPool, adminScopes: any) => { - let factQuery = knex('environment').countDistinct({ count: 'environment.id'}).innerJoin('project', 'environment.project', 'project.id'); + let factQuery = knex('environment').countDistinct({ count: 'environment.id' }).innerJoin('project', 'environment.project', 'project.id'); factQuery = buildConditionsForFactSearchQuery(filterDetails, factQuery, projectIdSubset, adminScopes); const rows = await query(sqlClientPool, factQuery.toString()); return rows[0].count; -} +}; -const buildConditionsForFactSearchQuery = (filterDetails: any, factQuery: any, projectIdSubset: number[], adminScopes: any, byPassLimits: boolean = false) => { +const buildConditionsForFactSearchQuery = (filterDetails: any, factQuery: any, projectIdSubset: number[], adminScopes: any, _byPassLimits: boolean = false) => { if (filterDetails.filters && filterDetails.filters.length > 0) { filterDetails.filters.forEach((filter, i) => { + const { lhsTarget, name } = filter; - let { lhsTarget, name } = filter; - - let tabName = `env${i}`; - if (lhsTarget == "project") { + const tabName = `env${i}`; + if (lhsTarget === 'project') { switch (name) { - case ("id"): + case ('id'): break; - case ("name"): + case ('name'): break; default: throw Error(`lhsTarget "${name}" unsupported`); } + } else if (filterDetails.filterConnective === 'AND') { + factQuery = factQuery.innerJoin(`environment_fact as ${tabName}`, 'environment.id', `${tabName}.environment`); } else { - if (filterDetails.filterConnective == 'AND') { - factQuery = factQuery.innerJoin(`environment_fact as ${tabName}`, 'environment.id', `${tabName}.environment`); - } else { - factQuery = factQuery.leftJoin(`environment_fact as ${tabName}`, 'environment.id', `${tabName}.environment`); - } + factQuery = factQuery.leftJoin(`environment_fact as ${tabName}`, 'environment.id', `${tabName}.environment`); } }); const builderFactory = (filter, i) => (builder) => { - let { lhsTarget, name, contains } = filter; - if (lhsTarget == "PROJECT") { + const { lhsTarget, name, contains } = filter; + if (lhsTarget === 'PROJECT') { builder = builder.andWhere(`project.${name}`, 'like', `${predicateRHSProcess('CONTAINS', contains)}`); } else { - let tabName = `env${i}`; + const tabName = `env${i}`; builder = builder.andWhere(`${tabName}.name`, '=', `${name}`); builder = builder.andWhere(`${tabName}.value`, 'like', `${predicateRHSProcess('CONTAINS', contains)}`); } @@ -707,14 +702,14 @@ const buildConditionsForFactSearchQuery = (filterDetails: any, factQuery: any, p factQuery.andWhere(innerBuilder => { filterDetails.filters.forEach((filter, i) => { - if (filterDetails.filterConnective == 'AND') { + if (filterDetails.filterConnective === 'AND') { innerBuilder = innerBuilder.andWhere(builderFactory(filter, i)); } else { innerBuilder = innerBuilder.orWhere(builderFactory(filter, i)); } }); return innerBuilder; - }) + }); } if (projectIdSubset && (!adminScopes.platformOwner && !adminScopes.platformViewer)) { @@ -722,12 +717,12 @@ const buildConditionsForFactSearchQuery = (filterDetails: any, factQuery: any, p } return factQuery; -} +}; function setQueryLimit(filterDetails: any, factQuery: any) { const DEFAULT_RESULTSET_SIZE = 25; - let { skip = 0, take = DEFAULT_RESULTSET_SIZE } = filterDetails; + const { skip = 0, take = DEFAULT_RESULTSET_SIZE } = filterDetails; factQuery = factQuery.limit(take).offset(skip); return factQuery; } diff --git a/services/api/src/resources/fact/sql.ts b/services/api/src/resources/fact/sql.ts index bc66dbf85e..ffc031bed5 100644 --- a/services/api/src/resources/fact/sql.ts +++ b/services/api/src/resources/fact/sql.ts @@ -1,28 +1,27 @@ import { knex } from '../../util/db'; const standardFactReturn = { - id: 'f.id', - environment: 'environment', - name: 'f.name', - value: 'value', - source: 'source', - description: 'description', - type: 'type', - category: 'category', - keyFact: 'keyFact', - service: 'service', + id: 'f.id', + environment: 'environment', + name: 'f.name', + value: 'value', + source: 'source', + description: 'description', + type: 'type', + category: 'category', + keyFact: 'keyFact', + service: 'service', }; export const Sql = { - selectFactByDatabaseId: (fid: number) => - knex('environment_fact as f') - .distinct(standardFactReturn) - .leftJoin('environment_fact_reference as r', 'r.fid', '=', 'f.id') - .where('f.id', fid) - .orderBy('f.id', 'asc') - .toString(), + selectFactByDatabaseId: (fid: number) => knex('environment_fact as f') + .distinct(standardFactReturn) + .leftJoin('environment_fact_reference as r', 'r.fid', '=', 'f.id') + .where('f.id', fid) + .orderBy('f.id', 'asc') + .toString(), selectFactsByEnvironmentId: ({ environmentId, keyFacts, limit }) => { - let q = knex('environment_fact as f') + const q = knex('environment_fact as f') .distinct(standardFactReturn) .leftJoin('environment_fact_reference as r', 'r.fid', '=', 'f.id') .where('environment', environmentId); @@ -35,61 +34,58 @@ export const Sql = { q.limit(limit); } - return q.orderBy('f.id', 'asc').toString() + return q.orderBy('f.id', 'asc').toString(); }, - insertFact: ({ environment, name, value, source, description, type, category, keyFact, service }) => - knex('environment_fact').insert({ environment, name, value, source, description, type, category, keyFact, service }).toString(), - deleteFact: (environment, name) => + insertFact: ({ + environment, name, value, source, description, type, category, keyFact, service, + }) => knex('environment_fact').insert({ + environment, name, value, source, description, type, category, keyFact, service, + }).toString(), + deleteFact: (environment, name) => knex('environment_fact') + .where({ + environment, + name, + }) + .del() + .toString(), + deleteFactsForEnvironment: (environment) => // Used when removing environments. knex('environment_fact') .where({ environment, - name }) .del() .toString(), - deleteFactsForEnvironment: (environment) => // Used when removing environments. - knex('environment_fact') - .where({ - environment - }) - .del() - .toString(), deleteFactsFromSource: (environment, source, service) => { - let query = knex('environment_fact') + let query = knex('environment_fact') .where({ environment, source }); - if(service != null) { - query = query.andWhere("service", "=", service) - } + if (service != null) { + query = query.andWhere('service', '=', service); + } query = query.del(); return query.toString(); }, - selectFactReferenceByDatabaseId: (id: number) => - knex('environment_fact_reference') - .where({ id }) - .toString(), - selectFactReferenceByNameAndEnvironmentId: (name: string, environmentId: number) => - knex('environment_fact_reference as r') - .select('r.*', 'f.environment') - .leftJoin('environment_fact as f', 'f.id', '=', 'r.fid') - .where('r.name', '=', name) - .andWhere('f.environment', '=', environmentId) - .toString(), - selectFactReferencesByFactId: (fid: number) => - knex('environment_fact_reference') - .where('fid', '=', fid) - .toString(), - insertFactReference: ({ fid, name }) => - knex('environment_fact_reference') - .insert({ fid, name }) - .onConflict(['name', 'fid']) - .merge() - .toString(), - deleteFactReferencesByFactId: (fid) => - knex('environment_fact_reference') - .where({ fid }) - .del() - .toString(), + selectFactReferenceByDatabaseId: (id: number) => knex('environment_fact_reference') + .where({ id }) + .toString(), + selectFactReferenceByNameAndEnvironmentId: (name: string, environmentId: number) => knex('environment_fact_reference as r') + .select('r.*', 'f.environment') + .leftJoin('environment_fact as f', 'f.id', '=', 'r.fid') + .where('r.name', '=', name) + .andWhere('f.environment', '=', environmentId) + .toString(), + selectFactReferencesByFactId: (fid: number) => knex('environment_fact_reference') + .where('fid', '=', fid) + .toString(), + insertFactReference: ({ fid, name }) => knex('environment_fact_reference') + .insert({ fid, name }) + .onConflict(['name', 'fid']) + .merge() + .toString(), + deleteFactReferencesByFactId: (fid) => knex('environment_fact_reference') + .where({ fid }) + .del() + .toString(), }; diff --git a/services/api/src/resources/file/resolvers.ts b/services/api/src/resources/file/resolvers.ts index 32a45d6721..873e4989ed 100644 --- a/services/api/src/resources/file/resolvers.ts +++ b/services/api/src/resources/file/resolvers.ts @@ -1,37 +1,38 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { createPresignedPost } from '@aws-sdk/s3-presigned-post'; +import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; +import { GetObjectCommand, DeleteObjectsCommand, ListObjectsCommand } from '@aws-sdk/client-s3'; +import { Upload } from '@aws-sdk/lib-storage'; +import { ResolverFn } from '..'; import { s3Client } from '../../clients/aws'; import { query } from '../../util/db'; import { Sql as taskSql } from '../task/sql'; import { s3Config } from '../../util/config'; import { AuditLog } from '../audit/types'; -import { AuditType } from '@lagoon/commons/dist/types'; -import { createPresignedPost } from "@aws-sdk/s3-presigned-post"; -import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; -import { GetObjectCommand, DeleteObjectsCommand, ListObjectsCommand } from "@aws-sdk/client-s3"; -import { Upload } from "@aws-sdk/lib-storage"; +import { AuditType } from '../../commons/types'; // if this is google cloud storage or not -const isGCS = process.env.S3_FILES_GCS || 'false' -const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files' +const isGCS = process.env.S3_FILES_GCS || 'false'; +const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files'; -async function generateDownloadLink(s3Key: string, userActivityLogger?: Function ) { +// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type +async function generateDownloadLink(s3Key: string, userActivityLogger?: Function) { const command = new GetObjectCommand({ Bucket: bucket, Key: s3Key }); if (typeof userActivityLogger === 'function') { const auditLog: AuditLog = { resource: { type: AuditType.FILE, - details: s3Key + details: s3Key, }, }; - userActivityLogger(`User requested a download link`, { + userActivityLogger('User requested a download link', { event: 'api:getSignedTaskUrl', payload: { Key: s3Key, - ...auditLog - } + ...auditLog, + }, }); } const signedUrl = await getSignedUrl(s3Client, command, { @@ -40,19 +41,17 @@ async function generateDownloadLink(s3Key: string, userActivityLogger?: Function return signedUrl; } -export const getDownloadLink: ResolverFn = async ({ s3Key }, input, { userActivityLogger }) => { - return generateDownloadLink(s3Key, userActivityLogger); -}; +export const getDownloadLink: ResolverFn = async ({ s3Key }, input, { userActivityLogger }) => generateDownloadLink(s3Key, userActivityLogger); export const getDownloadLinkByTaskFileId: ResolverFn = async ( root, { taskId, fileId }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rowsPerms = await query(sqlClientPool, taskSql.selectPermsForTask(taskId)); await hasPermission('task', 'view', { - project: R.path(['0', 'pid'], rowsPerms) + project: R.path(['0', 'pid'], rowsPerms), }); const command = new ListObjectsCommand({ Bucket: bucket, Prefix: `tasks/${taskId}` }); @@ -61,74 +60,74 @@ export const getDownloadLinkByTaskFileId: ResolverFn = async ( let count = 0; for (const obj of response.Contents) { count++; - if (count == fileId) { - const downloadUrl = await generateDownloadLink(obj.Key, userActivityLogger) + if (count === fileId) { + const downloadUrl = await generateDownloadLink(obj.Key, userActivityLogger); return downloadUrl; } } } - throw new Error(`File not found`); -} + throw new Error('File not found'); +}; export const getFilesByTaskId: ResolverFn = async ( { id: tid }, _args, - { sqlClientPool } + {}, ) => { const command = new ListObjectsCommand({ Bucket: bucket, Prefix: `tasks/${tid}` }); const response = await s3Client.send(command); if (response.Contents) { - let objects = []; + const objects = []; let count = 0; for (const obj of response.Contents) { count++; const date = new Date(obj.LastModified); - let path = require('path'); + const path = require('path'); objects.push({ id: count, filename: path.basename(obj.Key), s3Key: obj.Key, // fix the date for lagoon styled dates - created: date.toISOString().slice(0, 19).replace('T', ' ') - }) + created: date.toISOString().slice(0, 19).replace('T', ' '), + }); } return objects; } return []; -} +}; export const getTaskFileUploadForm: ResolverFn = async ( root, { input: { task, filename } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rowsPerms = await query( sqlClientPool, - taskSql.selectPermsForTask(task) + taskSql.selectPermsForTask(task), ); const projectId = R.path(['0', 'pid'], rowsPerms); await hasPermission('task', 'update', { - project: projectId + project: projectId, }); const s3_key = `tasks/${task}/${filename}`; - const signedurl = await generatePresignedPostUrl(s3_key) + const signedurl = await generatePresignedPostUrl(s3_key); const auditLog: AuditLog = { resource: { type: AuditType.FILE, - details: s3_key + details: s3_key, }, }; - userActivityLogger(`User requested file upload link`, { + userActivityLogger('User requested file upload link', { event: 'api:getTaskFileUploadForm', payload: { Key: s3_key, - ...auditLog - } + ...auditLog, + }, }); - return {postUrl: signedurl.url, formFields: signedurl.fields}; -} + return { postUrl: signedurl.url, formFields: signedurl.fields }; +}; async function generatePresignedPostUrl(key) { try { @@ -138,29 +137,29 @@ async function generatePresignedPostUrl(key) { Expires: 60, // 60 seconds Conditions: [ // limit upload size? maybe configurable at project/org level instead? - ["content-length-range", 0, 1099511627776], // 1tb + ['content-length-range', 0, 1099511627776], // 1tb ], }); return { url, fields }; - } catch (error) { - throw new Error(`Error generating presigned POST URL`); + } catch (_error) { + throw new Error('Error generating presigned POST URL'); } } export const uploadFilesForTask: ResolverFn = async ( root, { input: { task, files } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rowsPerms = await query( sqlClientPool, - taskSql.selectPermsForTask(task) + taskSql.selectPermsForTask(task), ); const projectId = R.path(['0', 'pid'], rowsPerms); await hasPermission('task', 'update', { - project: projectId + project: projectId, }); const resolvedFiles = await Promise.all(files); @@ -173,7 +172,7 @@ export const uploadFilesForTask: ResolverFn = async ( Bucket: bucket, Key: s3_key, Body: newFile.createReadStream(), - ...(isGCS == 'false' && { ACL: 'private' }), + ...(isGCS === 'false' && { ACL: 'private' }), }, }); @@ -190,9 +189,9 @@ export const uploadFilesForTask: ResolverFn = async ( project: '', event: 'api:uploadFilesForTask', payload: { - task - } - } + task, + }, + }, ); return task; @@ -201,31 +200,29 @@ export const uploadFilesForTask: ResolverFn = async ( export const deleteFilesForTask: ResolverFn = async ( root, { input: { id } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rowsPerms = await query(sqlClientPool, taskSql.selectPermsForTask(id)); await hasPermission('task', 'delete', { - project: R.path(['0', 'pid'], rowsPerms) + project: R.path(['0', 'pid'], rowsPerms), }); const command = new ListObjectsCommand({ Bucket: bucket, Prefix: `tasks/${id}` }); const response = await s3Client.send(command); if (response.Contents) { - let deleteObjects = []; - let count = 0; + const deleteObjects = []; for (const obj of response.Contents) { - count++; deleteObjects.push({ - Key: obj.Key - }) + Key: obj.Key, + }); } const deletecommand = new DeleteObjectsCommand({ Bucket: bucket, Delete: { Objects: deleteObjects, - Quiet: false - } + Quiet: false, + }, }); await s3Client.send(deletecommand); } @@ -234,8 +231,8 @@ export const deleteFilesForTask: ResolverFn = async ( project: '', event: 'api:deleteFilesForTask', payload: { - id - } + id, + }, }); return 'success'; diff --git a/services/api/src/resources/group/helpers.ts b/services/api/src/resources/group/helpers.ts index 036367f2b7..6b1455df4b 100644 --- a/services/api/src/resources/group/helpers.ts +++ b/services/api/src/resources/group/helpers.ts @@ -1,142 +1,134 @@ -import * as R from 'ramda'; import { Pool } from 'mariadb'; -import { asyncPipe } from '@lagoon/commons/dist/util/func'; import { query } from '../../util/db'; import { Sql } from './sql'; import { logger } from '../../loggers/logger'; -export const Helpers = (sqlClientPool: Pool) => { - return { - selectProjectIdsByGroupIDs: async (groupIds: string[]): Promise => { - const projectIdsArray = await query( - sqlClientPool, - Sql.selectProjectIdsByGroupIDs(groupIds) - ) - if (projectIdsArray[0]["projectIds"] != null) { - const values = projectIdsArray[0]["projectIds"].split(',').map(Number); - return values - } - return [] - }, - selectProjectIdsByGroupID: async (groupId: string): Promise => { - const projectIdsArray = await query( - sqlClientPool, - Sql.selectProjectIdsByGroupID(groupId) - ) - if (projectIdsArray[0]["projectIds"] != null) { - const values = projectIdsArray[0]["projectIds"].split(',').map(Number); - return values - } - return [] - }, - selectGroupsByProjectId: async (models, projectId: number) => { - const groupsArray = await query( - sqlClientPool, - Sql.selectGroupsByProjectId(projectId) - ) - if (groupsArray[0]["groupIds"] != null) { - const groups = groupsArray[0]["groupIds"].split(','); - let projectGroups = [] - // collect the groups now - for (const o in groups) { - projectGroups.push(await models.GroupModel.loadGroupById(groups[o])) - } - projectGroups.sort(function(a, b) { - return a.name.localeCompare(b.name); - }); - return projectGroups - } - return [] - }, - selectGroupsByOrganizationId: async (models, organizationId: number) => { - const groupsArray = await query( - sqlClientPool, - Sql.selectGroupsByOrganizationId(organizationId) - ) - if (groupsArray[0]["groupIds"] != null) { - const groups = groupsArray[0]["groupIds"].split(','); - let orgGroups = [] - // collect the groups now - for (const o in groups) { - orgGroups.push(await models.GroupModel.loadGroupById(groups[o])) - } - orgGroups.sort(function(a, b) { - return a.name.localeCompare(b.name); - }); - return orgGroups - } - return [] - }, - selectOrganizationByGroupId: async (groupId: string) => { - const organization = await query( - sqlClientPool, - Sql.selectOrganizationByGroupId(groupId) - ) - if (organization[0] != null) { - return organization[0] - } - return null - }, - selectOrganizationIdByGroupId: async (groupId: string) => { - const organization = await query( - sqlClientPool, - Sql.selectOrganizationByGroupId(groupId) - ) - if (organization[0] != null) { - return organization[0].organizationId - } - return null - }, - removeProjectFromGroup: async (projectId: number, groupId: string) => { - await query( - sqlClientPool, - Sql.removeProjectFromGroup(projectId, groupId) - ) - }, - addProjectToGroup: async (projectId: number, groupId: string) => { - try { - await query( - sqlClientPool, - Sql.addProjectToGroup({projectId, groupId}) - ); - } catch (err) { - return null - } - }, - addOrganizationToGroup: async (organizationId: number, groupId: string) => { - try { - await query( - sqlClientPool, - Sql.addOrganizationToGroup({organizationId, groupId}) - ); - } catch (err) { - return null - } - }, - removeGroupFromOrganization: async (groupId: string) => { - try { - // delete the reference for the group from the group_organization table - await query( - sqlClientPool, - Sql.deleteOrganizationGroup(groupId) - ); - } catch (err) { - return null - } - }, - deleteGroup: async (groupId: string) => { - await query( - sqlClientPool, - Sql.deleteOrganizationGroup(groupId) - ) - await query( - sqlClientPool, - Sql.deleteProjectGroup(groupId) - ) - try { - } catch (err) { - logger.info(`${err}`) - } - }, - }; -}; \ No newline at end of file +export const Helpers = (sqlClientPool: Pool) => ({ + selectProjectIdsByGroupIDs: async (groupIds: string[]): Promise => { + const projectIdsArray = await query( + sqlClientPool, + Sql.selectProjectIdsByGroupIDs(groupIds), + ); + if (projectIdsArray[0].projectIds != null) { + const values = projectIdsArray[0].projectIds.split(',').map(Number); + return values; + } + return []; + }, + selectProjectIdsByGroupID: async (groupId: string): Promise => { + const projectIdsArray = await query( + sqlClientPool, + Sql.selectProjectIdsByGroupID(groupId), + ); + if (projectIdsArray[0].projectIds != null) { + const values = projectIdsArray[0].projectIds.split(',').map(Number); + return values; + } + return []; + }, + selectGroupsByProjectId: async (models, projectId: number) => { + const groupsArray = await query( + sqlClientPool, + Sql.selectGroupsByProjectId(projectId), + ); + if (groupsArray[0].groupIds != null) { + const groups = groupsArray[0].groupIds.split(','); + const projectGroups = []; + // collect the groups now + for (const o in groups) { + projectGroups.push(await models.GroupModel.loadGroupById(groups[o])); + } + projectGroups.sort((a, b) => a.name.localeCompare(b.name)); + return projectGroups; + } + return []; + }, + selectGroupsByOrganizationId: async (models, organizationId: number) => { + const groupsArray = await query( + sqlClientPool, + Sql.selectGroupsByOrganizationId(organizationId), + ); + if (groupsArray[0].groupIds != null) { + const groups = groupsArray[0].groupIds.split(','); + const orgGroups = []; + // collect the groups now + for (const o in groups) { + orgGroups.push(await models.GroupModel.loadGroupById(groups[o])); + } + orgGroups.sort((a, b) => a.name.localeCompare(b.name)); + return orgGroups; + } + return []; + }, + selectOrganizationByGroupId: async (groupId: string) => { + const organization = await query( + sqlClientPool, + Sql.selectOrganizationByGroupId(groupId), + ); + if (organization[0] != null) { + return organization[0]; + } + return null; + }, + selectOrganizationIdByGroupId: async (groupId: string) => { + const organization = await query( + sqlClientPool, + Sql.selectOrganizationByGroupId(groupId), + ); + if (organization[0] != null) { + return organization[0].organizationId; + } + return null; + }, + removeProjectFromGroup: async (projectId: number, groupId: string) => { + await query( + sqlClientPool, + Sql.removeProjectFromGroup(projectId, groupId), + ); + }, + addProjectToGroup: async (projectId: number, groupId: string) => { + try { + await query( + sqlClientPool, + Sql.addProjectToGroup({ projectId, groupId }), + ); + } catch (_err) { + return null; + } + }, + addOrganizationToGroup: async (organizationId: number, groupId: string) => { + try { + await query( + sqlClientPool, + Sql.addOrganizationToGroup({ organizationId, groupId }), + ); + } catch (_err) { + return null; + } + }, + removeGroupFromOrganization: async (groupId: string) => { + try { + // delete the reference for the group from the group_organization table + await query( + sqlClientPool, + Sql.deleteOrganizationGroup(groupId), + ); + } catch (_err) { + return null; + } + }, + deleteGroup: async (groupId: string) => { + await query( + sqlClientPool, + Sql.deleteOrganizationGroup(groupId), + ); + await query( + sqlClientPool, + Sql.deleteProjectGroup(groupId), + ); + try { + } catch (err) { + logger.info(`${err}`); + } + }, +}); diff --git a/services/api/src/resources/group/resolvers.ts b/services/api/src/resources/group/resolvers.ts index 3b5f1206c8..7a46172a75 100644 --- a/services/api/src/resources/group/resolvers.ts +++ b/services/api/src/resources/group/resolvers.ts @@ -1,6 +1,6 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; import validator from 'validator'; +import { ResolverFn } from '..'; import { logger } from '../../loggers/logger'; import { isPatchEmpty } from '../../util/db'; import { GroupNotFoundError } from '../../models/group'; @@ -9,33 +9,33 @@ import { KeycloakUnauthorizedError } from '../../util/auth'; import { Helpers as organizationHelpers } from '../organization/helpers'; import { Helpers } from './helpers'; import { sqlClientPool } from '../../clients/sqlClient'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog, AuditResource } from '../audit/types'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; import { User, UserModel } from '../../models/user'; -const DISABLE_NON_ORGANIZATION_GROUP_CREATION = process.env.DISABLE_NON_ORGANIZATION_GROUP_CREATION || "false" +const DISABLE_NON_ORGANIZATION_GROUP_CREATION = process.env.DISABLE_NON_ORGANIZATION_GROUP_CREATION || 'false'; export const getAllGroups: ResolverFn = async ( root, { name, type }, - { hasPermission, models, keycloakGrant, keycloakUsersGroups, adminScopes } + { + models, keycloakGrant, keycloakUsersGroups, adminScopes, + }, ) => { // use the admin scope check instead of `hasPermission` for speed if (adminScopes.platformOwner || adminScopes.platformViewer) { try { - if (name) { const group = await models.GroupModel.loadGroupByName(name); return [group]; - } else { - const allGroups = await models.GroupModel.loadAllGroups(); - const groups = await models.GroupModel.transformKeycloakGroups(allGroups); - const filterFn = (key, val) => group => group[key].includes(val); - const filteredByName = groups.filter(filterFn('name', name)); - const filteredByType = groups.filter(filterFn('type', type)); - return name || type ? R.union(filteredByName, filteredByType) : groups; } + const allGroups = await models.GroupModel.loadAllGroups(); + const groups = await models.GroupModel.transformKeycloakGroups(allGroups); + const filterFn = (key, val) => group => group[key].includes(val); + const filteredByName = groups.filter(filterFn('name', name)); + const filteredByType = groups.filter(filterFn('type', type)); + return name || type ? R.union(filteredByName, filteredByType) : groups; } catch (err) { if (name && err instanceof GroupNotFoundError) { throw err; @@ -56,177 +56,183 @@ export const getAllGroups: ResolverFn = async ( const userGroups = await keycloakUsersGroups; if (name) { - return R.filter(R.propEq('name', name), userGroups); - } else { - return userGroups; + return R.filter(R.propEq(name, 'name'), userGroups); } - + return userGroups; }; // TODO: recursive lookups for groups in groups? export const getGroupFromGroupsById = async (id, groups) => { - const d = R.filter(R.propEq('id', id), groups); + const d = R.filter(R.propEq(id, 'id'), groups); if (d.length) { return d[0]; } for (const group in groups) { if (groups[group].groups.length) { - const d = R.filter(R.propEq('id', id), groups[group].groups) + const d = R.filter(R.propEq(id, 'id'), groups[group].groups); if (d.length) { return d[0]; } } } return {}; -} +}; export const getGroupFromGroupsByName = async (id, groups) => { - const d = R.filter(R.propEq('name', id), groups); + const d = R.filter(R.propEq(id, 'name'), groups); if (d.length) { return d[0]; } for (const group in groups) { if (groups[group].groups.length) { - const d = R.filter(R.propEq('name', id), groups[group].groups) + const d = R.filter(R.propEq(id, 'name'), groups[group].groups); if (d.length) { return d[0]; } } } return {}; -} +}; -export const getGroupRolesByUserId: ResolverFn =async ( +export const getGroupRolesByUserId: ResolverFn = async ( { id: uid }, _input, - { hasPermission, models, keycloakGrant, keycloakUsersGroups, adminScopes } + { + models, keycloakGrant, keycloakUsersGroups, adminScopes, + }, ) => { // use the admin scope check instead of `hasPermission` for speed if (adminScopes.platformOwner || adminScopes.platformViewer) { try { const queryUserGroups = await models.UserModel.getAllGroupsForUser(uid); - let groups = [] + const groups = []; for (const g in queryUserGroups) { - let group = {id: queryUserGroups[g].id, name: queryUserGroups[g].name, role: queryUserGroups[g].subGroups[0].realmRoles[0], groupType: null, organization: null} - if (queryUserGroups[g].attributes["type"]) { - group.groupType = queryUserGroups[g].attributes["type"][0] + const group = { + id: queryUserGroups[g].id, name: queryUserGroups[g].name, role: queryUserGroups[g].subGroups[0].realmRoles[0], groupType: null, organization: null, + }; + if (queryUserGroups[g].attributes.type) { + group.groupType = queryUserGroups[g].attributes.type[0]; } const org = await Helpers(sqlClientPool).selectOrganizationIdByGroupId(group.id); if (org) { - group.organization = org + group.organization = org; } - groups.push(group) + groups.push(group); } return groups; - } catch (err) { + } catch (_err) { if (!keycloakGrant) { logger.debug('No grant available for getGroupsByUserId'); return []; } } } - let groups = [] + const groups = []; for (const g in keycloakUsersGroups) { - let group = {id: keycloakUsersGroups[g].id, name: keycloakUsersGroups[g].name, role: keycloakUsersGroups[g].subGroups[0].realmRoles[0], groupType: null, organization: null} - if (keycloakUsersGroups[g].attributes["type"]) { - group.groupType = keycloakUsersGroups[g].attributes["type"][0] + const group = { + id: keycloakUsersGroups[g].id, name: keycloakUsersGroups[g].name, role: keycloakUsersGroups[g].subGroups[0].realmRoles[0], groupType: null, organization: null, + }; + if (keycloakUsersGroups[g].attributes.type) { + group.groupType = keycloakUsersGroups[g].attributes.type[0]; } const org = await Helpers(sqlClientPool).selectOrganizationIdByGroupId(keycloakUsersGroups[g].id); if (org) { - group.organization = org + group.organization = org; } - groups.push(group) + groups.push(group); } return groups; -} +}; export const getMembersByGroupId: ResolverFn = async ( { id }, _input, - { models } + { models }, ) => { // getMembersByGroupId is a field resolver that can't be queried directly, // permissions MUST be checked in a parent resolver. const group = await models.GroupModel.loadGroupById(id); const members = await models.GroupModel.getGroupMembership(group); return members; -} +}; export const getMemberCountByGroupId: ResolverFn = async ( { id }, _input, - { models } + { models }, ) => { // getMemberCountByGroupId is a field resolver that can't be queried directly, // permissions MUST be checked in a parent resolver. const group = await models.GroupModel.loadGroupById(id); const memberCount = await models.GroupModel.getGroupMemberCount(group); return memberCount; -} +}; export const getGroupsByProjectId: ResolverFn = async ( { id: pid }, _input, - { hasPermission, sqlClientPool, models, keycloakGrant, keycloakUsersGroups, adminScopes } + { + sqlClientPool, models, keycloakGrant, keycloakUsersGroups, adminScopes, + }, ) => { // use the admin scope check instead of `hasPermission` for speed if (adminScopes.platformOwner || adminScopes.platformViewer) { try { - const projectGroups = await Helpers(sqlClientPool).selectGroupsByProjectId(models, pid) + const projectGroups = await Helpers(sqlClientPool).selectGroupsByProjectId(models, pid); return projectGroups; - } catch (err) { + } catch (_err) { if (!keycloakGrant) { logger.debug('No grant available for getGroupsByProjectId'); return []; } } } else { - const projectGroups = await Helpers(sqlClientPool).selectGroupsByProjectId(models, pid) + const projectGroups = await Helpers(sqlClientPool).selectGroupsByProjectId(models, pid); const user = await models.UserModel.loadUserById( - keycloakGrant.access_token.content.sub + keycloakGrant.access_token.content.sub, ); // if this user is an owner of an organization, then also display org based groups to this user // when listing project groups const userGroups = keycloakUsersGroups; - const usersOrgs = R.defaultTo('', R.prop('lagoon-organizations', user.attributes)).toString() - const usersOrgsAdmin = R.defaultTo('', R.prop('lagoon-organizations-admin', user.attributes)).toString() - const usersOrgsViewer = R.defaultTo('', R.prop('lagoon-organizations-viewer', user.attributes)).toString() + const usersOrgs = R.defaultTo('', R.prop('lagoon-organizations', user.attributes)).toString(); + const usersOrgsAdmin = R.defaultTo('', R.prop('lagoon-organizations-admin', user.attributes)).toString(); + const usersOrgsViewer = R.defaultTo('', R.prop('lagoon-organizations-viewer', user.attributes)).toString(); - if (usersOrgs != "" ) { + if (usersOrgs !== '') { const uOrgs = usersOrgs.split(','); for (const userOrg of uOrgs) { - const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg) + const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg); for (const pGroup of orgGroups) { - userGroups.push(pGroup) + userGroups.push(pGroup); } } } - if (usersOrgsAdmin != "" ) { + if (usersOrgsAdmin !== '') { const uOrgs = usersOrgsAdmin.split(','); for (const userOrg of uOrgs) { - const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg) + const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg); for (const pGroup of orgGroups) { - userGroups.push(pGroup) + userGroups.push(pGroup); } } } - if (usersOrgsViewer != "" ) { + if (usersOrgsViewer !== '') { const uOrgs = usersOrgsViewer.split(','); for (const userOrg of uOrgs) { - const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg) + const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg); for (const pGroup of orgGroups) { - userGroups.push(pGroup) + userGroups.push(pGroup); } } } - let userProjectGroups = [] + const userProjectGroups = []; for (const ug of userGroups) { - const pg = projectGroups.find(i => i.id === ug.id) + const pg = projectGroups.find(i => i.id === ug.id); if (pg) { - userProjectGroups.push(pg) + userProjectGroups.push(pg); } } @@ -237,7 +243,9 @@ export const getGroupsByProjectId: ResolverFn = async ( export const getGroupsByUserId: ResolverFn = async ( { id: uid }, _input, - { hasPermission, models, keycloakGrant, keycloakUsersGroups, adminScopes } + { + models, keycloakGrant, keycloakUsersGroups, adminScopes, + }, ) => { // use the admin scope check instead of `hasPermission` for speed if (adminScopes.platformOwner || adminScopes.platformViewer) { @@ -245,7 +253,7 @@ export const getGroupsByUserId: ResolverFn = async ( const queryUserGroups = await models.UserModel.getAllGroupsForUser(uid); return queryUserGroups; - } catch (err) { + } catch (_err) { if (!keycloakGrant) { logger.debug('No grant available for getGroupsByUserId'); return []; @@ -260,7 +268,9 @@ export const getGroupsByUserId: ResolverFn = async ( export const getGroupByName: ResolverFn = async ( root, { name }, - { models, hasPermission, keycloakGrant, keycloakUsersGroups, adminScopes } + { + models, keycloakGrant, keycloakUsersGroups, adminScopes, + }, ) => { // use the admin scope check instead of `hasPermission` for speed if (adminScopes.platformOwner || adminScopes.platformViewer) { @@ -285,7 +295,7 @@ export const getGroupByName: ResolverFn = async ( } const userGroups = keycloakUsersGroups; - const group = R.head(R.filter(R.propEq('name', name), userGroups)); + const group = R.head(R.filter(R.propEq(name, 'name'), userGroups)); if (R.isEmpty(group)) { throw new GroupNotFoundError(`Group not found: ${name}`); @@ -297,7 +307,9 @@ export const getGroupByName: ResolverFn = async ( export const addGroup: ResolverFn = async ( _root, { input }, - { models, sqlClientPool, keycloakGrant, adminScopes, hasPermission, userActivityLogger} + { + models, sqlClientPool, keycloakGrant, adminScopes, hasPermission, userActivityLogger, + }, ) => { let attributes = null; // check if this is a group being added in an organization @@ -306,52 +318,52 @@ export const addGroup: ResolverFn = async ( if (input.organization != null) { const organizationData = await organizationHelpers(sqlClientPool).getOrganizationById(input.organization); if (organizationData === undefined) { - throw new Error(`Organization does not exist`) + throw new Error('Organization does not exist'); } await hasPermission('organization', 'addGroup', { - organization: input.organization + organization: input.organization, }); resource = { id: organizationData.id.toString(), - type: AuditType.ORGANIZATION - } + type: AuditType.ORGANIZATION, + }; - const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, input.organization) - let groupCount = 0 + const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, input.organization); + let groupCount = 0; for (const pGroup in orgGroups) { // project-default-groups don't count towards group quotas - if (orgGroups[pGroup].attributes["type"] != "project-default-group") { - groupCount++ + if (orgGroups[pGroup].attributes.type !== 'project-default-group') { + groupCount++; } } - if (groupCount >= organizationData.quotaGroup && organizationData.quotaGroup != -1) { + if (groupCount >= organizationData.quotaGroup && organizationData.quotaGroup !== -1) { throw new Error( - `This would exceed this organizations group quota; ${groupCount}/${organizationData.quotaGroup}` + `This would exceed this organizations group quota; ${groupCount}/${organizationData.quotaGroup}`, ); } attributes = { attributes: { // lagoon-organization attribute is added for legacy reasons only, theses values are stored in the api-db now - "lagoon-organization": [input.organization] - } - } + 'lagoon-organization': [input.organization], + }, + }; } else { // otherwise fall back - if (DISABLE_NON_ORGANIZATION_GROUP_CREATION == "false" || adminScopes.platformOwner) { + if (DISABLE_NON_ORGANIZATION_GROUP_CREATION === 'false' || adminScopes.platformOwner) { await hasPermission('group', 'add'); } else { throw new Error( - 'Group creation is restricted to organizations only' + 'Group creation is restricted to organizations only', ); } } if (validator.matches(input.name, /[^0-9a-z-]/)) { throw new Error( - 'Only lowercase characters, numbers and dashes allowed for name!' + 'Only lowercase characters, numbers and dashes allowed for name!', ); } @@ -362,12 +374,11 @@ export const addGroup: ResolverFn = async ( } const parentGroup = await models.GroupModel.loadGroupByIdOrName( - input.parentGroup + input.parentGroup, ); parentGroupId = parentGroup.id; } - const group = await models.GroupModel.addGroup({ name: input.name, parentGroupId, @@ -378,55 +389,53 @@ export const addGroup: ResolverFn = async ( // if the user is not an admin, or an organization add, then add the user as an owner to the group if (!adminScopes.platformOwner && !input.organization && keycloakGrant) { const user = await models.UserModel.loadUserById( - keycloakGrant.access_token.content.sub + keycloakGrant.access_token.content.sub, ); try { await models.GroupModel.addUserToGroup(user, group, 'owner'); } catch (err) { logger.error( - `Could not link requesting user to group ${group.name}: ${err.message}` + `Could not link requesting user to group ${group.name}: ${err.message}`, ); } } - const groupResource: AuditResource = { + const groupResource: AuditResource = { id: group.id, type: AuditType.GROUP, - details: group.name - } + details: group.name, + }; const auditLog: AuditLog = { - resource: resource, + resource, }; if (resource) { - auditLog.linkedResource = groupResource + auditLog.linkedResource = groupResource; } else { - auditLog.resource = groupResource + auditLog.resource = groupResource; } if (input.organization) { auditLog.organizationId = input.organization; } - userActivityLogger(`User added a group`, { + userActivityLogger('User added a group', { project: '', event: 'api:addGroup', payload: { data: { - group + group, }, ...auditLog, - } + }, }); return group; }; - - export const updateGroup: ResolverFn = async ( _root, { input: { group: groupInput, patch } }, - { models, hasPermission, userActivityLogger } + { models, hasPermission, userActivityLogger }, ) => { const group = await models.GroupModel.loadGroupByIdOrName(groupInput); const org = await Helpers(sqlClientPool).selectOrganizationIdByGroupId(group.id); @@ -434,15 +443,15 @@ export const updateGroup: ResolverFn = async ( if (org) { // if this is a group in an organization, check that the user updating it has permission to do so before deleting the group await hasPermission('organization', 'addGroup', { - organization: org + organization: org, }); resource = { id: org.toString(), - type: AuditType.ORGANIZATION - } + type: AuditType.ORGANIZATION, + }; } else { await hasPermission('group', 'update', { - group: group.id + group: group.id, }); } @@ -453,43 +462,43 @@ export const updateGroup: ResolverFn = async ( if (typeof patch.name === 'string') { if (validator.matches(patch.name, /[^0-9a-z-]/)) { throw new Error( - 'Only lowercase characters, numbers and dashes allowed for name!' + 'Only lowercase characters, numbers and dashes allowed for name!', ); } } const updatedGroup = await models.GroupModel.updateGroup({ id: group.id, - name: patch.name + name: patch.name, }); - const groupResource: AuditResource = { + const groupResource: AuditResource = { id: group.id, type: AuditType.GROUP, - details: group.name - } + details: group.name, + }; const auditLog: AuditLog = { - resource: resource, + resource, }; if (resource) { - auditLog.linkedResource = groupResource + auditLog.linkedResource = groupResource; } else { - auditLog.resource = groupResource + auditLog.resource = groupResource; } if (org) { auditLog.organizationId = org; } - userActivityLogger(`User updated a group`, { + userActivityLogger('User updated a group', { project: '', event: 'api:updateGroup', payload: { data: { patch, - updatedGroup + updatedGroup, }, ...auditLog, - } + }, }); return updatedGroup; @@ -498,7 +507,9 @@ export const updateGroup: ResolverFn = async ( export const deleteGroup: ResolverFn = async ( _root, { input: { group: groupInput } }, - { models, sqlClientPool, hasPermission, userActivityLogger } + { + models, sqlClientPool, hasPermission, userActivityLogger, + }, ) => { const group = await models.GroupModel.loadGroupByIdOrName(groupInput); const org = await Helpers(sqlClientPool).selectOrganizationIdByGroupId(group.id); @@ -506,46 +517,46 @@ export const deleteGroup: ResolverFn = async ( if (org) { // if this is a group in an organization, check that the user deleting it has permission to do so before deleting the group await hasPermission('organization', 'removeGroup', { - organization: org + organization: org, }); resource = { id: org.toString(), - type: AuditType.ORGANIZATION - } + type: AuditType.ORGANIZATION, + }; } else { await hasPermission('group', 'delete', { - group: group.id + group: group.id, }); } await models.GroupModel.deleteGroup(group.id); - const groupResource: AuditResource = { + const groupResource: AuditResource = { id: group.id, type: AuditType.GROUP, - details: group.name - } + details: group.name, + }; const auditLog: AuditLog = { - resource: resource, + resource, }; if (resource) { - auditLog.linkedResource = groupResource + auditLog.linkedResource = groupResource; } else { - auditLog.resource = groupResource + auditLog.resource = groupResource; } if (org) { auditLog.organizationId = org; } - userActivityLogger(`User deleted a group`, { + userActivityLogger('User deleted a group', { project: '', event: 'api:deleteGroup', payload: { data: { - group + group, }, ...auditLog, - } + }, }); return 'success'; @@ -553,8 +564,12 @@ export const deleteGroup: ResolverFn = async ( export const addUserToGroup: ResolverFn = async ( _root, - { input: { user: userInput, group: groupInput, role, inviteUser } }, - { models, hasPermission, userActivityLogger } + { + input: { + user: userInput, group: groupInput, role, inviteUser, + }, + }, + { models, hasPermission, userActivityLogger }, ) => { if (R.isEmpty(userInput)) { throw new Error('You must provide a user id or email'); @@ -572,14 +587,14 @@ export const addUserToGroup: ResolverFn = async ( // check if user already exists user = await models.UserModel.loadUserByIdOrEmail({ id: R.prop('id', userInput), - email: R.prop('email', userInput) + email: R.prop('email', userInput), }); } catch (e) { // capture the error - createUserErr = e + createUserErr = e; // if inviteuser is false, and there is an error, return the error if (createUserErr && !inviteUser) { - return createUserErr + return createUserErr; } } @@ -587,10 +602,10 @@ export const addUserToGroup: ResolverFn = async ( if (org) { // if this is a group in an organization, check that the user adding members to the group in this org is in the org await hasPermission('organization', 'addGroup', { - organization: org + organization: org, }); // only organization:addGroup will be able to "invite users" this way - if (createUserErr && createUserErr.message.includes("User not found") && inviteUser) { + if (createUserErr && createUserErr.message.includes('User not found') && inviteUser) { const email = R.prop('email', userInput); // check if email is provided if (email) { @@ -598,28 +613,28 @@ export const addUserToGroup: ResolverFn = async ( try { // then create the user account user = await models.UserModel.addUser({ - email: email, + email, username: email, }, true); logger.debug(`UserInvite: User created and password reset requested for ${email}`); } catch (e) { - return e + return e; } } else if (createUserErr) { // otherwise return whatever error loadUserByIdOrEmail returned - return createUserErr + return createUserErr; } } } else { await hasPermission('group', 'addUser', { - group: group.id + group: group.id, }); - if (createUserErr && createUserErr.message.includes("User not found") && inviteUser) { + if (createUserErr && createUserErr.message.includes('User not found') && inviteUser) { // otherwise return the error throw new Error('Cannot invite user to group that is not in an organization'); } else if (createUserErr) { // otherwise return whatever error loadUserByIdOrEmail returned - return createUserErr + return createUserErr; } } @@ -627,7 +642,7 @@ export const addUserToGroup: ResolverFn = async ( const updatedGroup = await models.GroupModel.addUserToGroup( user, group, - role + role, ); const auditLog: AuditLog = { @@ -645,18 +660,18 @@ export const addUserToGroup: ResolverFn = async ( if (org) { auditLog.organizationId = org; } - var emailDetails = await getGroupEmailDetails('api:addUserToGroup', group.name, models, user, {"Role": role}); - userActivityLogger(`User added a user to a group`, { + const emailDetails = await getGroupEmailDetails('api:addUserToGroup', group.name, models, user, { Role: role }); + userActivityLogger('User added a user to a group', { project: '', event: 'api:addUserToGroup', payload: { input: { - user: userInput, group: groupInput, role + user: userInput, group: groupInput, role, }, data: updatedGroup, ...auditLog, ...emailDetails, - } + }, }); return updatedGroup; @@ -665,7 +680,7 @@ export const addUserToGroup: ResolverFn = async ( export const removeUserFromGroup: ResolverFn = async ( _root, { input: { user: userInput, group: groupInput } }, - { models, hasPermission, userActivityLogger } + { models, hasPermission, userActivityLogger }, ) => { if (R.isEmpty(userInput)) { throw new Error('You must provide a user id or email'); @@ -673,7 +688,7 @@ export const removeUserFromGroup: ResolverFn = async ( const user = await models.UserModel.loadUserByIdOrEmail({ id: R.prop('id', userInput), - email: R.prop('email', userInput) + email: R.prop('email', userInput), }); if (R.isEmpty(groupInput)) { @@ -685,16 +700,16 @@ export const removeUserFromGroup: ResolverFn = async ( if (org) { // if this is a group in an organization, check that the user removing members from the group in this org is in the org await hasPermission('organization', 'addGroup', { - organization: org + organization: org, }); } else { await hasPermission('group', 'removeUser', { - group: group.id + group: group.id, }); } await models.GroupModel.removeUserFromGroup(user, group); - const updatedGroup = models.GroupModel.loadGroupById(group.id) + const updatedGroup = models.GroupModel.loadGroupById(group.id); const auditLog: AuditLog = { resource: { @@ -712,19 +727,19 @@ export const removeUserFromGroup: ResolverFn = async ( auditLog.organizationId = org; } - var emailDetails = await getGroupEmailDetails('api:removeUserFromGroup', group.name, models, user); + const emailDetails = await getGroupEmailDetails('api:removeUserFromGroup', group.name, models, user); - userActivityLogger(`User removed a user from a group`, { + userActivityLogger('User removed a user from a group', { project: '', event: 'api:removeUserFromGroup', payload: { input: { - user: userInput, group: groupInput + user: userInput, group: groupInput, }, data: updatedGroup, ...auditLog, ...emailDetails, - } + }, }); return updatedGroup; @@ -733,19 +748,21 @@ export const removeUserFromGroup: ResolverFn = async ( export const addGroupsToProject: ResolverFn = async ( _root, { input: { project: projectInput, groups: groupsInput } }, - { models, sqlClientPool, hasPermission, userActivityLogger } + { + models, sqlClientPool, hasPermission, userActivityLogger, + }, ) => { const project = await projectHelpers(sqlClientPool).getProjectByProjectInput( - projectInput + projectInput, ); if (project.organization != null) { // this project is in an organization, limit it to organization groups only await hasPermission('organization', 'addGroup', { - organization: project.organization + organization: project.organization, }); } else { await hasPermission('project', 'addGroup', { - project: project.id + project: project.id, }); } @@ -766,10 +783,10 @@ export const addGroupsToProject: ResolverFn = async ( throw new Error('Group must be in same organization as the project'); } if (org && project.organization != null) { - if (project.organization == org) { + if (project.organization === org) { // if this is a group in an organization, check that the user removing members from the group in this org is in the org await hasPermission('organization', 'addGroup', { - organization: org + organization: org, }); } else { throw new Error('Group must be in same organization as the project'); @@ -786,21 +803,21 @@ export const addGroupsToProject: ResolverFn = async ( }, linkedResource: { type: AuditType.GROUP, - details: `multiple groups`, + details: 'multiple groups', }, }; if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User synced groups to a project`, { + userActivityLogger('User synced groups to a project', { project: '', event: 'api:addGroupsToProject', payload: { input: { - project: projectInput, groups: groupsInput + project: projectInput, groups: groupsInput, }, ...auditLog, - } + }, }); return await projectHelpers(sqlClientPool).getProjectById(project.id); @@ -809,16 +826,18 @@ export const addGroupsToProject: ResolverFn = async ( export const getAllProjectsByGroupId: ResolverFn = async ( root, input, - context + context, ) => getAllProjectsInGroup(root, { input: { id: root.id } }, { ...context }); export const getAllProjectsInGroup: ResolverFn = async ( _root, { input: groupInput }, - { models, sqlClientPool, hasPermission, keycloakGrant, adminScopes } + { + models, sqlClientPool, keycloakGrant, adminScopes, + }, ) => { const { - GroupModel: { loadGroupByIdOrName, getProjectsFromGroupAndSubgroups } + GroupModel: { loadGroupByIdOrName, getProjectsFromGroupAndSubgroups }, } = models; // use the admin scope check instead of `hasPermission` for speed @@ -827,8 +846,7 @@ export const getAllProjectsInGroup: ResolverFn = async ( // get group from all keycloak groups apollo context const group = await loadGroupByIdOrName(groupInput); const projectIdsArray = await getProjectsFromGroupAndSubgroups(group); - return projectIdsArray.map(async id => - projectHelpers(sqlClientPool).getProjectByProjectInput({ id }) + return projectIdsArray.map(async id => projectHelpers(sqlClientPool).getProjectByProjectInput({ id }), ); } catch (err) { if (err instanceof GroupNotFoundError) { @@ -845,61 +863,61 @@ export const getAllProjectsInGroup: ResolverFn = async ( if (!keycloakGrant) { logger.debug('No grant available for getAllProjectsInGroup'); return []; - } else { - const group = await models.GroupModel.loadGroupByIdOrName(groupInput); - const userGroups = await models.UserModel.getAllGroupsForUser(keycloakGrant.access_token.content.sub); - const user = await models.UserModel.loadUserById( - keycloakGrant.access_token.content.sub - ); - const usersOrgs = R.defaultTo('', R.prop('lagoon-organizations', user.attributes)).toString() - const usersOrgsAdmin = R.defaultTo('', R.prop('lagoon-organizations-admin', user.attributes)).toString() - const usersOrgsViewer = R.defaultTo('', R.prop('lagoon-organizations-viewer', user.attributes)).toString() - if (usersOrgs != "" ) { - const uOrgs = usersOrgs.split(','); - for (const userOrg of uOrgs) { - const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg) - for (const pGroup of orgGroups) { - userGroups.push(pGroup) - } + } + const group = await models.GroupModel.loadGroupByIdOrName(groupInput); + const userGroups = await models.UserModel.getAllGroupsForUser(keycloakGrant.access_token.content.sub); + const user = await models.UserModel.loadUserById( + keycloakGrant.access_token.content.sub, + ); + const usersOrgs = R.defaultTo('', R.prop('lagoon-organizations', user.attributes)).toString(); + const usersOrgsAdmin = R.defaultTo('', R.prop('lagoon-organizations-admin', user.attributes)).toString(); + const usersOrgsViewer = R.defaultTo('', R.prop('lagoon-organizations-viewer', user.attributes)).toString(); + if (usersOrgs !== '') { + const uOrgs = usersOrgs.split(','); + for (const userOrg of uOrgs) { + const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg); + for (const pGroup of orgGroups) { + userGroups.push(pGroup); } } - if (usersOrgsAdmin != "" ) { - const uOrgs = usersOrgsAdmin.split(','); - for (const userOrg of uOrgs) { - const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg) - for (const pGroup of orgGroups) { - userGroups.push(pGroup) - } + } + if (usersOrgsAdmin !== '') { + const uOrgs = usersOrgsAdmin.split(','); + for (const userOrg of uOrgs) { + const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg); + for (const pGroup of orgGroups) { + userGroups.push(pGroup); } } - if (usersOrgsViewer != "" ) { - const uOrgs = usersOrgsViewer.split(','); - for (const userOrg of uOrgs) { - const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg) - for (const pGroup of orgGroups) { - userGroups.push(pGroup) - } + } + if (usersOrgsViewer !== '') { + const uOrgs = usersOrgsViewer.split(','); + for (const userOrg of uOrgs) { + const orgGroups = await Helpers(sqlClientPool).selectGroupsByOrganizationId(models, userOrg); + for (const pGroup of orgGroups) { + userGroups.push(pGroup); } } - // @ts-ignore - if (!R.contains(group.name, R.pluck('name', userGroups))) { - logger.debug('No grant available for getAllProjectsInGroup'); - return []; - } - const projectIdsArray = await getProjectsFromGroupAndSubgroups(group); - return projectIdsArray.map(async id => - projectHelpers(sqlClientPool).getProjectByProjectInput({ id }) - ); } + // @ts-ignore + if (!R.contains(group.name, R.pluck('name', userGroups))) { + logger.debug('No grant available for getAllProjectsInGroup'); + return []; + } + const projectIdsArray = await getProjectsFromGroupAndSubgroups(group); + return projectIdsArray.map(async id => projectHelpers(sqlClientPool).getProjectByProjectInput({ id }), + ); }; export const removeGroupsFromProject: ResolverFn = async ( _root, { input: { project: projectInput, groups: groupsInput } }, - { models, sqlClientPool, hasPermission, userActivityLogger } + { + models, sqlClientPool, hasPermission, userActivityLogger, + }, ) => { const project = await projectHelpers(sqlClientPool).getProjectByProjectInput( - projectInput + projectInput, ); // check if this is a group being removed by an organization @@ -907,16 +925,16 @@ export const removeGroupsFromProject: ResolverFn = async ( if (project.organization != null) { const organizationData = await organizationHelpers(sqlClientPool).getOrganizationById(project.organization); if (organizationData === undefined) { - throw new Error(`Organization does not exist`) + throw new Error('Organization does not exist'); } await hasPermission('organization', 'removeGroup', { - organization: project.organization + organization: project.organization, }); } else { // otherwise fall back await hasPermission('project', 'removeGroup', { - project: project.id + project: project.id, }); } @@ -943,40 +961,39 @@ export const removeGroupsFromProject: ResolverFn = async ( }, linkedResource: { type: AuditType.GROUP, - details: `multiple groups`, + details: 'multiple groups', }, }; if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User synced groups to a project`, { + userActivityLogger('User synced groups to a project', { project: '', event: 'api:removeGroupsFromProject', payload: { input: { - project: projectInput, groups: groupsInput + project: projectInput, groups: groupsInput, }, ...auditLog, - } + }, }); return await projectHelpers(sqlClientPool).getProjectById(project.id); }; - // This function retrieves email details for SSH key actions based on user preferences // it's really a convenience function to avoid code duplication async function getGroupEmailDetails(action, groupname, models: { UserModel: UserModel;}, user: User, additionalDetails = {}) { - var emailDetails = {}; + let emailDetails = {}; try { - let dbUserDetails = await models.UserModel.getFullUserDetails(user); - if (dbUserDetails && dbUserDetails['optEmailGroupRole'] == true) { + const dbUserDetails = await models.UserModel.getFullUserDetails(user) as any; + if (dbUserDetails && dbUserDetails.optEmailGroupRole === true) { emailDetails = { userActionEmailDetails: { Name: user.email, Email: user.email, GroupName: groupname, - ...additionalDetails + ...additionalDetails, }, }; } @@ -987,7 +1004,7 @@ async function getGroupEmailDetails(action, groupname, models: { UserModel: User user.id, action, {}, - `Error while trying to get full user DB details for user(id|email) (${user.id}|${user.email}): error: ${e.message}` + `Error while trying to get full user DB details for user(id|email) (${user.id}|${user.email}): error: ${e.message}`, ); } return emailDetails; diff --git a/services/api/src/resources/group/sql.ts b/services/api/src/resources/group/sql.ts index 47f254cb6a..53c2d69e4a 100644 --- a/services/api/src/resources/group/sql.ts +++ b/services/api/src/resources/group/sql.ts @@ -1,76 +1,66 @@ import { knex } from '../../util/db'; -import { logger } from '../../loggers/logger'; - export const Sql = { - selectProjectIdsByGroupIDs: (groupIds: string[]) => - knex('kc_group_projects') - .whereIn('group_id', groupIds) - .select(knex.raw('group_concat(project_id) as project_ids')) - .toString(), - selectProjectIdsByGroupID: (groupId: string) => - knex('kc_group_projects') - .where('group_id', groupId) - .select(knex.raw('group_concat(project_id) as project_ids')) - .toString(), - selectGroupsByProjectId: (projectId: number) => - knex('kc_group_projects') - .where('project_id', projectId) - .select(knex.raw('group_concat(group_id) as group_ids')) - .toString(), - selectGroupsByOrganizationId: (organizationId: number) => - knex('kc_group_organization') - .where('organization_id', organizationId) - .select(knex.raw('group_concat(group_id) as group_ids')) - .toString(), - selectOrganizationByGroupId: (groupId: string) => - knex('kc_group_organization') - .where('group_id', groupId) - .toString(), - addProjectToGroup: (input) => { - const { - id, - projectId, - groupId, - } = input; - return knex('kc_group_projects').insert({ - id, - projectId, - groupId, - }) - .onConflict('id') - .merge() - .toString(); - }, - removeProjectFromGroup: (projectId: number, groupId: string) => - knex('kc_group_projects') - .where('project_id', projectId) - .andWhere('group_id', groupId) - .del() - .toString(), - addOrganizationToGroup: (input) => { - const { - id, - groupId, - organizationId, - } = input; - return knex('kc_group_organization').insert({ - id, - groupId, - organizationId - }) - .onConflict('id') - .merge() - .toString(); - }, - deleteProjectGroup: (groupId: string) => - knex('kc_group_projects') - .where('group_id', groupId) - .del() - .toString(), - deleteOrganizationGroup: (groupId: string) => - knex('kc_group_organization') - .where('group_id', groupId) - .del() - .toString(), -}; \ No newline at end of file + selectProjectIdsByGroupIDs: (groupIds: string[]) => knex('kc_group_projects') + .whereIn('group_id', groupIds) + .select(knex.raw('group_concat(project_id) as project_ids')) + .toString(), + selectProjectIdsByGroupID: (groupId: string) => knex('kc_group_projects') + .where('group_id', groupId) + .select(knex.raw('group_concat(project_id) as project_ids')) + .toString(), + selectGroupsByProjectId: (projectId: number) => knex('kc_group_projects') + .where('project_id', projectId) + .select(knex.raw('group_concat(group_id) as group_ids')) + .toString(), + selectGroupsByOrganizationId: (organizationId: number) => knex('kc_group_organization') + .where('organization_id', organizationId) + .select(knex.raw('group_concat(group_id) as group_ids')) + .toString(), + selectOrganizationByGroupId: (groupId: string) => knex('kc_group_organization') + .where('group_id', groupId) + .toString(), + addProjectToGroup: (input) => { + const { + id, + projectId, + groupId, + } = input; + return knex('kc_group_projects').insert({ + id, + projectId, + groupId, + }) + .onConflict('id') + .merge() + .toString(); + }, + removeProjectFromGroup: (projectId: number, groupId: string) => knex('kc_group_projects') + .where('project_id', projectId) + .andWhere('group_id', groupId) + .del() + .toString(), + addOrganizationToGroup: (input) => { + const { + id, + groupId, + organizationId, + } = input; + return knex('kc_group_organization').insert({ + id, + groupId, + organizationId, + }) + .onConflict('id') + .merge() + .toString(); + }, + deleteProjectGroup: (groupId: string) => knex('kc_group_projects') + .where('group_id', groupId) + .del() + .toString(), + deleteOrganizationGroup: (groupId: string) => knex('kc_group_organization') + .where('group_id', groupId) + .del() + .toString(), +}; diff --git a/services/api/src/resources/insight/helpers.ts b/services/api/src/resources/insight/helpers.ts index a91cd38e1d..5aa9d3743c 100644 --- a/services/api/src/resources/insight/helpers.ts +++ b/services/api/src/resources/insight/helpers.ts @@ -1,29 +1,29 @@ import sha1 from 'sha1'; export const convertBytesToHumanFileSize = (size) => { - if (size < 1024) return size + ' B'; + if (size < 1024) return `${size} B`; - let i = Math.floor(Math.log(size) / Math.log(1024)); - let num: number | string = (size / Math.pow(1024, i)); - let round = Math.round(num); + const i = Math.floor(Math.log(size) / Math.log(1024)); + let num: number | string = (size / 1024 ** i); + const round = Math.round(num); num = round < 10 ? num.toFixed(2) : round < 100 ? num.toFixed(1) : round; - return `${num} ${'KMGTPEZY'[i-1]}B`; -} + return `${num} ${'KMGTPEZY'[i - 1]}B`; +}; export const getEnvironmentName = ( environmentData, - projectData + projectData, ) => { // we need to get the safename of the environment from when it was created - const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g,'-') - var environmentName = makeSafe(environmentData.name) - var overlength = 58 - projectData.name.length; - if ( environmentName.length > overlength ) { - var hash = sha1(environmentName).substring(0,4) - environmentName = environmentName.substring(0, overlength-5) - environmentName = environmentName.concat('-' + hash) + const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g, '-'); + let environmentName = makeSafe(environmentData.name); + const overlength = 58 - projectData.name.length; + if (environmentName.length > overlength) { + const hash = sha1(environmentName).substring(0, 4); + environmentName = environmentName.substring(0, overlength - 5); + environmentName = environmentName.concat(`-${hash}`); } - return environmentName ? environmentName : "" -} \ No newline at end of file + return environmentName || ''; +}; diff --git a/services/api/src/resources/insight/resolvers.ts b/services/api/src/resources/insight/resolvers.ts index cea397102f..71cf510c2a 100644 --- a/services/api/src/resources/insight/resolvers.ts +++ b/services/api/src/resources/insight/resolvers.ts @@ -3,25 +3,26 @@ import { getEnvironmentName, convertBytesToHumanFileSize } from './helpers'; import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; -const { S3Client, ListObjectsV2Command, GetObjectCommand } = require('@aws-sdk/client-s3'); -const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); import { s3Config } from '../../util/config'; import { AuditLog } from '../audit/types'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; + +const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); +const { S3Client, ListObjectsV2Command, GetObjectCommand } = require('@aws-sdk/client-s3'); // s3 config -const accessKeyId = process.env.S3_FILES_ACCESS_KEY_ID || 'minio' -const secretAccessKey = process.env.S3_FILES_SECRET_ACCESS_KEY || 'minio123' -const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files' -const region = process.env.S3_FILES_REGION || 'us-east-1' -const s3Origin = process.env.S3_FILES_HOST || 'http://docker.for.mac.localhost:9000' +const accessKeyId = process.env.S3_FILES_ACCESS_KEY_ID || 'minio'; +const secretAccessKey = process.env.S3_FILES_SECRET_ACCESS_KEY || 'minio123'; +const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files'; +const region = process.env.S3_FILES_REGION || 'us-east-1'; +const s3Origin = process.env.S3_FILES_HOST || 'http://docker.for.mac.localhost:9000'; const config = { origin: s3Origin, - accessKeyId: accessKeyId, - secretAccessKey: secretAccessKey, - region: region, - bucket: bucket + accessKeyId, + secretAccessKey, + region, + bucket, }; const s3Client = new S3Client({ @@ -32,12 +33,12 @@ const s3Client = new S3Client({ }, region: config.region, forcePathStyle: true, - signatureVersion: 'v4' + signatureVersion: 'v4', }); // Get insights files directly from the bucket export const getInsightsBucketFiles = async ({ prefix }) => { - try { + try { const data = await s3Client.send(new ListObjectsV2Command({ Bucket: bucket, Prefix: prefix })); if (!data.Contents) { @@ -45,28 +46,26 @@ export const getInsightsBucketFiles = async ({ prefix }) => { } return await JSON.parse(JSON.stringify(data.Contents)); - } - catch (e) { - throw new Error(`Error retrieving bucket items - ${e.message}`) - } -} + } catch (e) { + throw new Error(`Error retrieving bucket items - ${e.message}`); + } +}; export const getInsightsDownloadUrl: ResolverFn = async ( - { fileId, environment, file }, + { _fileId, environment, file }, _args, - { sqlClientPool, userActivityLogger } + { sqlClientPool, userActivityLogger }, ) => { - const environmentData = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(parseInt(environment)); const projectData = await projectHelpers(sqlClientPool).getProjectById( - environmentData.project + environmentData.project, ); - let environmentName = getEnvironmentName(environmentData, projectData); + const environmentName = getEnvironmentName(environmentData, projectData); - try { + try { const s3Key = `insights/${projectData.name}/${environmentName}/${file}`; if (typeof userActivityLogger === 'function') { @@ -78,44 +77,44 @@ export const getInsightsDownloadUrl: ResolverFn = async ( if (projectData.organization) { auditLog.organizationId = projectData.organization; } - userActivityLogger(`User requested a download link`, { + userActivityLogger('User requested a download link', { event: 'api:getSignedInsightsUrl', payload: { Bucket: bucket, Key: s3Key, ...auditLog, - } + }, }); } const command = new GetObjectCommand({ Bucket: bucket, Key: s3Key }); return await getSignedUrl(s3Client, command, { expiresIn: s3Config.signedLinkExpiration }); - } catch (e) { - return `Error while creating download link - ${e.Error || 'Unknown error'}` - } -} + } catch (e) { + return `Error while creating download link - ${e.Error || 'Unknown error'}`; + } +}; export const getInsightsFileData: ResolverFn = async ( { fileId, environment, file }, _args, - { sqlClientPool } + { sqlClientPool }, ) => { if (!fileId) { return null; } const environmentData = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(parseInt(environment)); const projectData = await projectHelpers(sqlClientPool).getProjectById( - environmentData.project + environmentData.project, ); - let environmentName = getEnvironmentName(environmentData, projectData); + const environmentName = getEnvironmentName(environmentData, projectData); try { - let insightsFile = 'insights/'+projectData.name+'/'+environmentName+'/'+file + const insightsFile = `insights/${projectData.name}/${environmentName}/${file}`; const data = await s3Client.send(new GetObjectCommand({ Bucket: bucket, Key: insightsFile })); if (!data) { @@ -132,45 +131,44 @@ export const getInsightsFileData: ResolverFn = async ( export const getInsightsFilesByEnvironmentId: ResolverFn = async ( { id: eid }, - { name, limit }, - { sqlClientPool, hasPermission, adminScopes } + { _name, _limit }, + { sqlClientPool, hasPermission, adminScopes }, ) => { - if (!eid) { - throw "No Environment ID given."; + throw 'No Environment ID given.'; } const environmentData = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(parseInt(eid)); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: environmentData.project + project: environmentData.project, }); } const projectData = await projectHelpers(sqlClientPool).getProjectById( - environmentData.project + environmentData.project, ); const environmentName = getEnvironmentName(environmentData, projectData); - const insightsItems = await getInsightsBucketFiles({ prefix: 'insights/'+projectData.name+'/'+environmentName+'/'}); + const insightsItems = await getInsightsBucketFiles({ prefix: `insights/${projectData.name}/${environmentName}/` }); const files = await Promise.all(insightsItems.map(async (file, index) => { - const fileName = file.Key ? file.Key.split("/").pop() : ""; - const type = fileName ? fileName.split('-')[0] : ""; - const service = fileName ? fileName.split('-').at('-1').split('.')[0] : ""; + const fileName = file.Key ? file.Key.split('/').pop() : ''; + const type = fileName ? fileName.split('-')[0] : ''; + const service = fileName ? fileName.split('-').at('-1').split('.')[0] : ''; return { - id: index+1, + id: index + 1, file: fileName, size: file.Size && convertBytesToHumanFileSize(file.Size), created: file.LastModified && file.LastModified, - service: service, - type: type, + service, + type, environment: eid, - } + }; })); return files; diff --git a/services/api/src/resources/lagoon/resolvers.ts b/services/api/src/resources/lagoon/resolvers.ts index 13887c6860..373d35fea8 100644 --- a/services/api/src/resources/lagoon/resolvers.ts +++ b/services/api/src/resources/lagoon/resolvers.ts @@ -1,5 +1,4 @@ import { getConfigFromEnv } from '../../util/config'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; -export const getLagoonVersion: ResolverFn = async () => - getConfigFromEnv('LAGOON_VERSION', 'unknown'); +export const getLagoonVersion: ResolverFn = async () => getConfigFromEnv('LAGOON_VERSION', 'unknown'); diff --git a/services/api/src/resources/notification/__snapshots__/notification.test.js.snap b/services/api/src/resources/notification/__snapshots__/notification.test.js.snap index c4ef1dea05..f0566c0266 100644 --- a/services/api/src/resources/notification/__snapshots__/notification.test.js.snap +++ b/services/api/src/resources/notification/__snapshots__/notification.test.js.snap @@ -9,10 +9,10 @@ exports[`Sql deleteProjectNotification should create a proper query 1`] = ` AND nt.name = 'some_slack'" `; -exports[`Sql selectAssignedProjectNotificationByName should create proper statement 1`] = `"select \`nt\`.*, \`pn\`.*, 'slack' as type from \`project_notification\` as \`pn\` JOIN notification_slack AS nt ON pn.nid = nt.id AND pn.type = 'slack' where \`nt\`.\`name\` = 'slack_foo'"`; +exports[`Sql selectAssignedProjectNotificationByName should create proper statement 1`] = `"select \`nt\`.*, \`pn\`.*, 'slack' as orig_type from \`project_notification\` as \`pn\` JOIN notification_slack AS nt ON pn.nid = nt.id AND pn.type = 'slack' and pn.content_type = 'deployment' where \`nt\`.\`name\` = 'slack_foo'"`; exports[`Sql selectCustomer should create a proper query 1`] = `"select * from \`notification_slack\` where \`name\` = 'n1'"`; -exports[`Sql selectNotificationsByTypeByProjectId should create a proper query 1`] = `"select \`nt\`.*, \`pn\`.\`type\` from \`project_notification\` as \`pn\` JOIN notification_slack AS nt ON pn.nid = nt.id AND pn.type = 'slack' where \`pn\`.\`pid\` = 3"`; +exports[`Sql selectNotificationsByTypeByProjectId should create a proper query 1`] = `"select \`nt\`.*, \`pn\`.\`type\`, \`pn\`.\`content_type\` as \`content_type\`, \`pn\`.\`notification_severity_threshold\` as \`notification_severity_threshold\` from \`project_notification\` as \`pn\` JOIN notification_slack AS nt ON pn.nid = nt.id AND pn.type = 'slack' AND pn.content_type = 'deployment' where \`pn\`.\`pid\` = 3 and \`pn\`.\`notification_severity_threshold\` >= 0"`; -exports[`Sql updateNotificationSlack should create a proper query 1`] = `"update \`notification_email\` set \`name\` = 'test', \`webhook\` = 'new-webhook' where \`name\` = 'n1'"`; +exports[`Sql updateNotificationSlack should create a proper query 1`] = `"update \`notification_slack\` set \`name\` = 'test', \`webhook\` = 'new-webhook' where \`name\` = 'n1'"`; diff --git a/services/api/src/resources/notification/helpers.ts b/services/api/src/resources/notification/helpers.ts index e65f4f9169..e06770ebfa 100644 --- a/services/api/src/resources/notification/helpers.ts +++ b/services/api/src/resources/notification/helpers.ts @@ -17,74 +17,74 @@ export const Helpers = (sqlClientPool: Pool) => ({ selectNotificationsByProjectId: async ( { project }: { project: number }, ) => { - let input = {pid: project, type: "slack"} + const input = { pid: project, type: 'slack' }; // get all the notifications for the projects const slacks = await query( sqlClientPool, - Sql.selectNotificationsByTypeByProjectId(input) + Sql.selectNotificationsByTypeByProjectId(input), ); - input.type = "rocketchat" + input.type = 'rocketchat'; const rcs = await query( sqlClientPool, - Sql.selectNotificationsByTypeByProjectId(input) + Sql.selectNotificationsByTypeByProjectId(input), ); - input.type = "microsoftteams" + input.type = 'microsoftteams'; const teams = await query( sqlClientPool, - Sql.selectNotificationsByTypeByProjectId(input) + Sql.selectNotificationsByTypeByProjectId(input), ); - input.type = "email" + input.type = 'email'; const email = await query( sqlClientPool, - Sql.selectNotificationsByTypeByProjectId(input) + Sql.selectNotificationsByTypeByProjectId(input), ); - input.type = "webhook" + input.type = 'webhook'; const webhook = await query( sqlClientPool, - Sql.selectNotificationsByTypeByProjectId(input) + Sql.selectNotificationsByTypeByProjectId(input), ); - let result = [...slacks, ...rcs, ...teams, ...email, ...webhook] + const result = [...slacks, ...rcs, ...teams, ...email, ...webhook]; - return result + return result; }, removeAllNotificationsFromProject: async ( { project }: { project: number }, ) => { - await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, "slack")); - await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, "rocketchat")); - await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, "microsoftteams")); - await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, "email")); - await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, "webhook")); + await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, 'slack')); + await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, 'rocketchat')); + await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, 'microsoftteams')); + await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, 'email')); + await query(sqlClientPool, Sql.deleteProjectNotificationByProjectId(project, 'webhook')); }, selectAllNotifications: async () => { - let type = "slack" + let type = 'slack'; // get all notifications const slacks = await query( sqlClientPool, - Sql.selectAllNotifications(type) + Sql.selectAllNotifications(type), ); - type = "rocketchat" + type = 'rocketchat'; const rcs = await query( sqlClientPool, - Sql.selectAllNotifications(type) + Sql.selectAllNotifications(type), ); - type = "microsoftteams" + type = 'microsoftteams'; const teams = await query( sqlClientPool, - Sql.selectAllNotifications(type) + Sql.selectAllNotifications(type), ); - type = "email" + type = 'email'; const email = await query( sqlClientPool, - Sql.selectAllNotifications(type) + Sql.selectAllNotifications(type), ); - type = "webhook" + type = 'webhook'; const webhook = await query( sqlClientPool, - Sql.selectAllNotifications(type) + Sql.selectAllNotifications(type), ); - let result = [...slacks, ...rcs, ...teams, ...email, ...webhook] + const result = [...slacks, ...rcs, ...teams, ...email, ...webhook]; - return result + return result; }, }); diff --git a/services/api/src/resources/notification/resolvers.ts b/services/api/src/resources/notification/resolvers.ts index 84e4ebc751..5d95ebdc85 100644 --- a/services/api/src/resources/notification/resolvers.ts +++ b/services/api/src/resources/notification/resolvers.ts @@ -1,5 +1,5 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { query, isPatchEmpty, knex } from '../../util/db'; import { Helpers as projectHelpers } from '../project/helpers'; import { Helpers as organizationHelpers } from '../organization/helpers'; @@ -8,33 +8,33 @@ import { Sql } from './sql'; import { Sql as projectSql } from '../project/sql'; import { NOTIFICATION_CONTENT_TYPE, - NOTIFICATION_SEVERITY_THRESHOLD + NOTIFICATION_SEVERITY_THRESHOLD, } from './defaults'; import { notificationIntToContentType, - notificationContentTypeToInt -} from '@lagoon/commons/dist/notificationCommons'; + notificationContentTypeToInt, +} from '../../commons/notificationCommons'; import { sqlClientPool } from '../../clients/sqlClient'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog, AuditResource } from '../audit/types'; -const DISABLE_NON_ORGANIZATION_NOTIFICATION_ASSIGNMENT = process.env.DISABLE_NON_ORGANIZATION_NOTIFICATION_ASSIGNMENT || "false" +const DISABLE_NON_ORGANIZATION_NOTIFICATION_ASSIGNMENT = process.env.DISABLE_NON_ORGANIZATION_NOTIFICATION_ASSIGNMENT || 'false'; const addNotificationGeneric = async (sqlClientPool, userActivityLogger, notificationTable, type, input) => { const createSql = knex(notificationTable).insert(input).toString(); let insertId: number; try { - ({insertId} = await query(sqlClientPool, createSql)); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + ({ insertId } = await query(sqlClientPool, createSql)); + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding notification ${input.name}. Notification already exists` - ) + `Error adding notification ${input.name}. Notification already exists`, + ); } else { throw new Error(error.message); } - }; + } const notificationResource: AuditResource = { id: insertId.toString(), @@ -56,135 +56,136 @@ const addNotificationGeneric = async (sqlClientPool, userActivityLogger, notific project: '', event: `api:addNotification${type}`, payload: { - ...auditLog - } + ...auditLog, + }, }); return await query(sqlClientPool, knex(notificationTable).where('id', insertId).toString()); -} +}; const checkOrgNotificationPermission = async (hasPermission, input, adminScopes) => { if (input.organization != null) { const organizationData = await organizationHelpers(sqlClientPool).getOrganizationById(input.organization); if (organizationData === undefined) { - throw new Error(`Organization does not exist`) + throw new Error('Organization does not exist'); } await hasPermission('organization', 'addNotification', { - organization: input.organization + organization: input.organization, }); const orgNotifications = await organizationHelpers(sqlClientPool).getNotificationsForOrganizationId(input.organization); - if (orgNotifications.length >= organizationData.quotaNotification && organizationData.quotaNotification != -1) { + if (orgNotifications.length >= organizationData.quotaNotification && organizationData.quotaNotification !== -1) { throw new Error( - `This would exceed this organizations notification quota; ${orgNotifications.length}/${organizationData.quotaNotification}` + `This would exceed this organizations notification quota; ${orgNotifications.length}/${organizationData.quotaNotification}`, ); } + } else if (DISABLE_NON_ORGANIZATION_NOTIFICATION_ASSIGNMENT === 'false' || adminScopes.platformOwner) { + await hasPermission('notification', 'add'); } else { - if (DISABLE_NON_ORGANIZATION_NOTIFICATION_ASSIGNMENT == "false" || adminScopes.platformOwner) { - await hasPermission('notification', 'add'); - } else { - throw new Error( - 'Project notification assignment is restricted to organizations only' - ); - } + throw new Error( + 'Project notification assignment is restricted to organizations only', + ); } -} +}; export const addNotificationMicrosoftTeams: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, adminScopes, userActivityLogger} + { + sqlClientPool, hasPermission, adminScopes, userActivityLogger, + }, ) => { - await checkOrgNotificationPermission(hasPermission, input, adminScopes) + await checkOrgNotificationPermission(hasPermission, input, adminScopes); return R.path([0], await addNotificationGeneric(sqlClientPool, userActivityLogger, 'notification_microsoftteams', 'MicrosoftTeams', input)); }; export const addNotificationEmail: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, adminScopes, userActivityLogger} + { + sqlClientPool, hasPermission, adminScopes, userActivityLogger, + }, ) => { - await checkOrgNotificationPermission(hasPermission, input, adminScopes) + await checkOrgNotificationPermission(hasPermission, input, adminScopes); return R.path([0], await addNotificationGeneric(sqlClientPool, userActivityLogger, 'notification_email', 'Email', input)); }; export const addNotificationRocketChat: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, adminScopes, userActivityLogger} + { + sqlClientPool, hasPermission, adminScopes, userActivityLogger, + }, ) => { - await checkOrgNotificationPermission(hasPermission, input, adminScopes) + await checkOrgNotificationPermission(hasPermission, input, adminScopes); return R.path([0], await addNotificationGeneric(sqlClientPool, userActivityLogger, 'notification_rocketchat', 'RocketChat', input)); }; export const addNotificationSlack: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, adminScopes, userActivityLogger} + { + sqlClientPool, hasPermission, adminScopes, userActivityLogger, + }, ) => { - await checkOrgNotificationPermission(hasPermission, input, adminScopes) + await checkOrgNotificationPermission(hasPermission, input, adminScopes); return R.path([0], await addNotificationGeneric(sqlClientPool, userActivityLogger, 'notification_slack', 'Slack', input)); }; -export const addNotificationWebhook: ResolverFn = async (root, { input }, { sqlClientPool, hasPermission, adminScopes, userActivityLogger}) => { - await checkOrgNotificationPermission(hasPermission, input, adminScopes) +export const addNotificationWebhook: ResolverFn = async (root, { input }, { + sqlClientPool, hasPermission, adminScopes, userActivityLogger, +}) => { + await checkOrgNotificationPermission(hasPermission, input, adminScopes); return R.path([0], await addNotificationGeneric(sqlClientPool, userActivityLogger, 'notification_webhook', 'Webhook', input)); }; - export const addNotificationToProject: ResolverFn = async ( root, { input: unformattedInput }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const input = [ - R.over( - R.lensProp('notificationSeverityThreshold'), - notificationContentTypeToInt - ) + (obj: any) => ({ ...obj, notificationSeverityThreshold: notificationContentTypeToInt(obj.notificationSeverityThreshold) }), ].reduce( - (argumentsToProcess, functionToApply) => - functionToApply(argumentsToProcess), - unformattedInput + (argumentsToProcess, functionToApply) => functionToApply(argumentsToProcess), + unformattedInput, ); const pid = await projectHelpers(sqlClientPool).getProjectIdByName( - input.project + input.project, ); - const projectData = await projectHelpers(sqlClientPool).getProjectById(pid) + const projectData = await projectHelpers(sqlClientPool).getProjectById(pid); const rows = await query( sqlClientPool, - Sql.selectProjectNotification(input) + Sql.selectProjectNotification(input), ); const projectNotification = R.path([0], rows) as any; - let noproject = false + let noproject = false; if (!projectNotification) { - noproject = true + noproject = true; } // if a project is configured with organizations // only organization permissions should be able to add notifications to it if (projectNotification.oid != null) { await hasPermission('organization', 'addNotification', { - organization: projectNotification.oid + organization: projectNotification.oid, }); } else { await hasPermission('project', 'addNotification', { - project: pid + project: pid, }); } if (noproject) { throw new Error( - `Could not find notification '${input.notificationName}' of type '${input.notificationType}'` + `Could not find notification '${input.notificationName}' of type '${input.notificationType}'`, ); } projectNotification.notificationType = input.notificationType; - projectNotification.contentType = - input.contentType || NOTIFICATION_CONTENT_TYPE; - projectNotification.notificationSeverityThreshold = - input.notificationSeverityThreshold || NOTIFICATION_SEVERITY_THRESHOLD; + projectNotification.contentType = input.contentType || NOTIFICATION_CONTENT_TYPE; + projectNotification.notificationSeverityThreshold = input.notificationSeverityThreshold || NOTIFICATION_SEVERITY_THRESHOLD; const auditLog: AuditLog = { resource: { @@ -206,60 +207,60 @@ export const addNotificationToProject: ResolverFn = async ( payload: { projectNotification, ...auditLog, - } + }, }); await query( sqlClientPool, - Sql.createProjectNotification(projectNotification) + Sql.createProjectNotification(projectNotification), ); const select = await query( sqlClientPool, - Sql.selectProjectById(projectNotification.pid) + Sql.selectProjectById(projectNotification.pid), ); const project = R.path([0], select); return project; }; const deleteNotificationGeneric = async (sqlClientPool, notificationTableName, typename, name) => { - let res = await query(sqlClientPool, knex(notificationTableName).where('name', name).toString()); - let nsid = R.path([0, "id"], res); - if(!nsid) { + const res = await query(sqlClientPool, knex(notificationTableName).where('name', name).toString()); + const nsid = R.path([0, 'id'], res); + if (!nsid) { throw Error(`Notification of name ${name} not found`); } - await query(sqlClientPool, knex('project_notification').where('nid', nsid).andWhere('type', typename).delete().toString()); + await query(sqlClientPool, knex('project_notification').where('nid', nsid).andWhere('type', typename).delete() + .toString()); await query(sqlClientPool, knex(notificationTableName).where('name', name).delete().toString()); -} +}; const checkNotificationRemovePermissions = async (check, hasPermission) => { if (R.prop(0, check).organization != null) { await hasPermission('organization', 'removeNotification', { - organization: R.prop(0, check).organization + organization: R.prop(0, check).organization, }); } else { await hasPermission('notification', 'delete'); } -} - +}; export const deleteNotificationMicrosoftTeams: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const { name } = input; const check = await query( sqlClientPool, - Sql.selectNotificationMicrosoftTeamsByName(name) + Sql.selectNotificationMicrosoftTeamsByName(name), ); - await checkNotificationRemovePermissions(check, hasPermission) + await checkNotificationRemovePermissions(check, hasPermission); const nids = await Helpers(sqlClientPool).getAssignedNotificationIds({ name, - type: 'microsoftteams' + type: 'microsoftteams', }); - if (R.length(nids) > 0) { + if (R.length(nids as any[]) > 0) { throw new Error("Can't delete notification linked to projects"); } @@ -272,21 +273,21 @@ export const deleteNotificationMicrosoftTeams: ResolverFn = async ( export const deleteNotificationEmail: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const { name } = input; const check = await query( sqlClientPool, - Sql.selectNotificationEmailByName(name) + Sql.selectNotificationEmailByName(name), ); - await checkNotificationRemovePermissions(check, hasPermission) + await checkNotificationRemovePermissions(check, hasPermission); const nids = await Helpers(sqlClientPool).getAssignedNotificationIds({ name, - type: 'email' + type: 'email', }); - if (R.length(nids) > 0) { + if (R.length(nids as any[]) > 0) { throw new Error("Can't delete notification linked to projects"); } @@ -299,25 +300,25 @@ export const deleteNotificationEmail: ResolverFn = async ( export const deleteNotificationRocketChat: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const { name } = input; const check = await query( sqlClientPool, - Sql.selectNotificationRocketChatByName(name) + Sql.selectNotificationRocketChatByName(name), ); - await checkNotificationRemovePermissions(check, hasPermission) + await checkNotificationRemovePermissions(check, hasPermission); const nids = await Helpers(sqlClientPool).getAssignedNotificationIds({ name, - type: 'rocketchat' + type: 'rocketchat', }); - if (R.length(nids) > 0) { + if (R.length(nids as any[]) > 0) { throw new Error("Can't delete notification linked to projects"); } - await deleteNotificationGeneric(sqlClientPool, "notification_rocketchat", "rocketchat", name); + await deleteNotificationGeneric(sqlClientPool, 'notification_rocketchat', 'rocketchat', name); // TODO: maybe check rows for changed result return 'success'; @@ -326,31 +327,30 @@ export const deleteNotificationRocketChat: ResolverFn = async ( export const deleteNotificationSlack: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const { name } = input; const check = await query( sqlClientPool, - Sql.selectNotificationSlackByName(name) + Sql.selectNotificationSlackByName(name), ); - await checkNotificationRemovePermissions(check, hasPermission) + await checkNotificationRemovePermissions(check, hasPermission); const nids = await Helpers(sqlClientPool).getAssignedNotificationIds({ name, - type: 'slack' + type: 'slack', }); - if (R.length(nids) > 0) { + if (R.length(nids as any[]) > 0) { throw new Error("Can't delete notification linked to projects"); } - await deleteNotificationGeneric(sqlClientPool, "notification_slack", "slack", name); + await deleteNotificationGeneric(sqlClientPool, 'notification_slack', 'slack', name); // TODO: maybe check rows for changed result return 'success'; }; - export const deleteNotificationWebhook: ResolverFn = async ( root, { input }, @@ -362,48 +362,45 @@ export const deleteNotificationWebhook: ResolverFn = async ( const { name } = input; const check = await query( sqlClientPool, - Sql.selectNotificationWebhookByName(name) + Sql.selectNotificationWebhookByName(name), ); - await checkNotificationRemovePermissions(check, hasPermission) + await checkNotificationRemovePermissions(check, hasPermission); const nids = await Helpers(sqlClientPool).getAssignedNotificationIds({ name, type: 'webhook', }); - if (R.length(nids) > 0) { + if (R.length(nids as any[]) > 0) { throw new Error("Can't delete notification linked to projects"); } - await deleteNotificationGeneric(sqlClientPool, "notification_webhook", "webhook", name); + await deleteNotificationGeneric(sqlClientPool, 'notification_webhook', 'webhook', name); // TODO: maybe check rows for changed result return 'success'; }; - - export const removeNotificationFromProject: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const select = await query( sqlClientPool, - projectSql.selectProjectByName(input.project) + projectSql.selectProjectByName(input.project), ); const project = R.path([0], select) as any; - // if a project is configured with organizations // only organization permissions should be able to remove notifications if (project.organization != null) { await hasPermission('organization', 'removeNotification', { - organization: project.organization + organization: project.organization, }); } else { await hasPermission('project', 'addNotification', { - project: project.id + project: project.id, }); } @@ -417,27 +414,23 @@ const NOTIFICATION_TYPES = ['slack', 'rocketchat', 'microsoftteams', 'email', 'w export const getNotificationsByProjectId: ResolverFn = async ( { id: pid }, unformattedArgs, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { await hasPermission('notification', 'view', { - project: pid + project: pid, }); const args = [ - R.over( - R.lensProp('notificationSeverityThreshold'), - notificationContentTypeToInt - ) + (obj: any) => ({ ...obj, notificationSeverityThreshold: notificationContentTypeToInt(obj.notificationSeverityThreshold) }), ].reduce( - (argumentsToProcess, functionToApply) => - functionToApply(argumentsToProcess), - unformattedArgs + (argumentsToProcess, functionToApply) => functionToApply(argumentsToProcess), + unformattedArgs, ); const { type: argsType, contentType = NOTIFICATION_CONTENT_TYPE, - notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD + notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD, } = args; // Types to collect notifications from all different @@ -445,60 +438,49 @@ export const getNotificationsByProjectId: ResolverFn = async ( const types = argsType == null ? NOTIFICATION_TYPES : [argsType.toLowerCase()]; const results = await Promise.all( - types.map(type => - query( - sqlClientPool, - Sql.selectNotificationsByTypeByProjectId({ - type, - pid, - contentType, - notificationSeverityThreshold - }) - ) - ) + types.map(type => query( + sqlClientPool, + Sql.selectNotificationsByTypeByProjectId({ + type, + pid, + contentType, + notificationSeverityThreshold, + }), + ), + ), ); - let resultArray = results.reduce((acc, rows) => { + const resultArray = results.reduce((acc, rows) => { if (rows == null) { return acc; } return R.concat(acc, rows); }, []); - return resultArray.map(e => - R.over( - R.lensProp('notificationSeverityThreshold'), - notificationIntToContentType, - e - ) + return resultArray.map(e => ({ ...e, notificationSeverityThreshold: notificationIntToContentType((e as any).notificationSeverityThreshold) }), ); }; export const getNotificationsByOrganizationId: ResolverFn = async ( { id: oid }, unformattedArgs, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { - await hasPermission('organization', 'viewNotification', { - organization: oid + organization: oid, }); const args = [ - R.over( - R.lensProp('notificationSeverityThreshold'), - notificationContentTypeToInt - ) + (obj: any) => ({ ...obj, notificationSeverityThreshold: notificationContentTypeToInt(obj.notificationSeverityThreshold) }), ].reduce( - (argumentsToProcess, functionToApply) => - functionToApply(argumentsToProcess), - unformattedArgs + (argumentsToProcess, functionToApply) => functionToApply(argumentsToProcess), + unformattedArgs, ); const { type: argsType, - contentType = NOTIFICATION_CONTENT_TYPE, - notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD + _contentType = NOTIFICATION_CONTENT_TYPE, + _notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD, } = args; // Types to collect notifications from all different @@ -506,76 +488,70 @@ export const getNotificationsByOrganizationId: ResolverFn = async ( const types = argsType == null ? NOTIFICATION_TYPES : [argsType.toLowerCase()]; const results = await Promise.all( - types.map(type => - organizationHelpers(sqlClientPool).getNotificationsByTypeForOrganizationId(oid, type) - ) + types.map(type => organizationHelpers(sqlClientPool).getNotificationsByTypeForOrganizationId(oid, type), + ), ); - let resultArray = results.reduce((acc, rows) => { + const resultArray = results.reduce((acc, rows) => { if (rows == null) { return acc; } return R.concat(acc, rows); }, []); - return resultArray.map(e => - R.over( - R.lensProp('notificationSeverityThreshold'), - notificationIntToContentType, - e - ) + return resultArray.map(e => ({ ...e, notificationSeverityThreshold: notificationIntToContentType((e as any).notificationSeverityThreshold) }), ); }; const checkNotificationUpdatePermissions = async (check, hasPermission) => { if (R.prop(0, check).organization != null) { await hasPermission('organization', 'updateNotification', { - organization: R.prop(0, check).organization + organization: R.prop(0, check).organization, }); } else { await hasPermission('notification', 'update'); } -} +}; const checkNotificationExists = (name, check) => { - if (R.find(R.propEq('name', `${name}`))(check) == undefined) { + if (R.find(R.propEq(`${name}`, 'name'))(check) === undefined) { throw new Error(`No notification found for ${name}`); } -} +}; export const updateNotificationMicrosoftTeams: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { if (isPatchEmpty(input)) { throw new Error('input.patch requires at least 1 attribute'); } - const { name, patch: {name: newName = ''} } = input; + const { name, patch: { name: newName = '' } } = input; const check = await query( sqlClientPool, - Sql.selectNotificationMicrosoftTeamsByName(name) + Sql.selectNotificationMicrosoftTeamsByName(name), ); checkNotificationExists(name, check); - await checkNotificationUpdatePermissions(check, hasPermission) + await checkNotificationUpdatePermissions(check, hasPermission); try { await query(sqlClientPool, Sql.updateNotificationMicrosoftTeams(input)); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists` - ) + `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists`, + ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectNotificationMicrosoftTeamsByName(newName ? newName : name) + Sql.selectNotificationMicrosoftTeamsByName(newName || name), ); return R.prop(0, rows); @@ -592,31 +568,31 @@ export const updateNotificationWebhook: ResolverFn = async ( if (isPatchEmpty(input)) { throw new Error('input.patch requires at least 1 attribute'); } - const { name, patch: {name: newName = ''} } = input; + const { name, patch: { name: newName = '' } } = input; const check = await query( sqlClientPool, - Sql.selectNotificationWebhookByName(name) + Sql.selectNotificationWebhookByName(name), ); checkNotificationExists(name, check); - await checkNotificationUpdatePermissions(check, hasPermission) + await checkNotificationUpdatePermissions(check, hasPermission); try { await query(sqlClientPool, Sql.updateNotificationWebhook(input)); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists` - ) + `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists`, + ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectNotificationWebhookByName(newName ? newName : name), + Sql.selectNotificationWebhookByName(newName || name), ); return R.prop(0, rows); @@ -625,36 +601,36 @@ export const updateNotificationWebhook: ResolverFn = async ( export const updateNotificationEmail: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { if (isPatchEmpty(input)) { throw new Error('input.patch requires at least 1 attribute'); } - const { name, patch: {name: newName = ''} } = input; + const { name, patch: { name: newName = '' } } = input; const check = await query( sqlClientPool, - Sql.selectNotificationEmailByName(name) + Sql.selectNotificationEmailByName(name), ); - checkNotificationExists(name, check) + checkNotificationExists(name, check); - await checkNotificationUpdatePermissions(check, hasPermission) + await checkNotificationUpdatePermissions(check, hasPermission); try { await query(sqlClientPool, Sql.updateNotificationEmail(input)); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists` - ) + `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists`, + ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectNotificationEmailByName(newName ? newName : name) + Sql.selectNotificationEmailByName(newName || name), ); return R.prop(0, rows); @@ -663,36 +639,36 @@ export const updateNotificationEmail: ResolverFn = async ( export const updateNotificationRocketChat: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { if (isPatchEmpty(input)) { throw new Error('input.patch requires at least 1 attribute'); } - const { name, patch: {name: newName = ''} } = input; + const { name, patch: { name: newName = '' } } = input; const check = await query( sqlClientPool, - Sql.selectNotificationRocketChatByName(name) + Sql.selectNotificationRocketChatByName(name), ); - checkNotificationExists(name, check) + checkNotificationExists(name, check); - await checkNotificationUpdatePermissions(check, hasPermission) + await checkNotificationUpdatePermissions(check, hasPermission); try { await query(sqlClientPool, Sql.updateNotificationRocketChat(input)); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists` - ) + `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists`, + ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectNotificationRocketChatByName(newName ? newName : name) + Sql.selectNotificationRocketChatByName(newName || name), ); return R.prop(0, rows); @@ -701,36 +677,36 @@ export const updateNotificationRocketChat: ResolverFn = async ( export const updateNotificationSlack: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { if (isPatchEmpty(input)) { throw new Error('input.patch requires at least 1 attribute'); } - const { name, patch: {name: newName = ''} } = input; + const { name, patch: { name: newName = '' } } = input; const check = await query( sqlClientPool, - Sql.selectNotificationSlackByName(name) + Sql.selectNotificationSlackByName(name), ); - checkNotificationExists(name, check) + checkNotificationExists(name, check); - await checkNotificationUpdatePermissions(check, hasPermission) + await checkNotificationUpdatePermissions(check, hasPermission); try { await query(sqlClientPool, Sql.updateNotificationSlack(input)); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists` - ) + `Error renaming notification ${input.name}. Notification ${input.patch.name} already exists`, + ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectNotificationSlackByName(newName ? newName : name) + Sql.selectNotificationSlackByName(newName || name), ); return R.prop(0, rows); @@ -739,11 +715,11 @@ export const updateNotificationSlack: ResolverFn = async ( export const getAllNotifications: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { await hasPermission('notification', 'viewAll'); const rows = await Helpers(sqlClientPool).selectAllNotifications(); return rows; -}; \ No newline at end of file +}; diff --git a/services/api/src/resources/notification/sql.ts b/services/api/src/resources/notification/sql.ts index 7af3fdc615..486397f5b3 100644 --- a/services/api/src/resources/notification/sql.ts +++ b/services/api/src/resources/notification/sql.ts @@ -1,8 +1,7 @@ -import { logger } from '../../loggers/logger'; import { knex } from '../../util/db'; import { NOTIFICATION_CONTENT_TYPE, - NOTIFICATION_SEVERITY_THRESHOLD + NOTIFICATION_SEVERITY_THRESHOLD, } from './defaults'; export const Sql = { @@ -12,7 +11,7 @@ export const Sql = { notificationType, nid, contentType = NOTIFICATION_CONTENT_TYPE, - notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD + notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD, } = input; return knex('project_notification') @@ -21,7 +20,7 @@ export const Sql = { type: notificationType, nid, content_type: contentType, - notification_severity_threshold: notificationSeverityThreshold + notification_severity_threshold: notificationSeverityThreshold, }) .toString(); }, @@ -31,16 +30,15 @@ export const Sql = { return knex('project_notification AS pn') .joinRaw( `JOIN notification_${type} AS nt ON pn.nid = nt.id AND pn.type = :type and pn.content_type = :content_type`, - { type: type, content_type: contentType } + { type, content_type: contentType }, ) .where('nt.name', '=', name) .select('nt.*', 'pn.*', knex.raw('? as orig_type', [type])) .toString(); }, - selectAllNotifications: (type: string) => - knex(`notification_${type}`) - .select('*', knex.raw(`'${type}' as type`)) - .toString(), + selectAllNotifications: (type: string) => knex(`notification_${type}`) + .select('*', knex.raw(`'${type}' as type`)) + .toString(), deleteProjectNotification: input => { const deleteQuery = knex.raw( `DELETE pn @@ -51,8 +49,8 @@ export const Sql = { AND nt.name = :notificationName`, { ...input, - notificationTable: `notification_${input.notificationType}` - } + notificationTable: `notification_${input.notificationType}`, + }, ); return deleteQuery.toString(); @@ -67,26 +65,25 @@ export const Sql = { { pid: id, notificationType: type, - notificationTable: `notification_${type}` - } + notificationTable: `notification_${type}`, + }, ); return deleteQuery.toString(); }, - selectProjectById: input => - knex('project') - .select('*') - .where({ - 'project.id': input - }) - .toString(), + selectProjectById: input => knex('project') + .select('*') + .where({ + 'project.id': input, + }) + .toString(), selectProjectByName: input => { const { project } = input; return knex('project') .select('*') .where({ - 'project.name': project + 'project.name': project, }) .toString(); }, @@ -95,13 +92,13 @@ export const Sql = { project, notificationType, notificationName, - contentType = NOTIFICATION_CONTENT_TYPE + _contentType = NOTIFICATION_CONTENT_TYPE, } = input; - let ret = knex({ p: 'project', nt: `notification_${notificationType}` }) - .where({ 'p.name': project }) - .andWhere({ 'nt.name': notificationName }) - .select({ pid: 'p.id', nid: 'nt.id', oid: 'p.organization' }) - .toString(); + const ret = knex({ p: 'project', nt: `notification_${notificationType}` }) + .where({ 'p.name': project }) + .andWhere({ 'nt.name': notificationName }) + .select({ pid: 'p.id', nid: 'nt.id', oid: 'p.organization' }) + .toString(); return ret; }, updateNotificationMicrosoftTeams: input => { @@ -149,11 +146,11 @@ export const Sql = { type, pid, contentType = NOTIFICATION_CONTENT_TYPE, - notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD + notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD, } = input; - let selectQuery = knex('project_notification AS pn').joinRaw( + const selectQuery = knex('project_notification AS pn').joinRaw( `JOIN notification_${type} AS nt ON pn.nid = nt.id AND pn.type = :type AND pn.content_type = :contentType`, - { type, contentType } + { type, contentType }, ); return selectQuery @@ -161,13 +158,13 @@ export const Sql = { .where( 'pn.notification_severity_threshold', '>=', - notificationSeverityThreshold + notificationSeverityThreshold, ) .select( 'nt.*', 'pn.type', 'pn.content_type as contentType', - 'pn.notification_severity_threshold as notificationSeverityThreshold' + 'pn.notification_severity_threshold as notificationSeverityThreshold', ) .toString(); }, @@ -175,61 +172,50 @@ export const Sql = { const { type, oid, - contentType = NOTIFICATION_CONTENT_TYPE, - notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD + _contentType = NOTIFICATION_CONTENT_TYPE, + _notificationSeverityThreshold = NOTIFICATION_SEVERITY_THRESHOLD, } = input; - let selectQuery = knex(`notification_${type} as nt`); + const selectQuery = knex(`notification_${type} as nt`); return selectQuery .where('nt.organization', '=', oid) .select( 'nt.*', - knex.raw(`'${type}' as type`) + knex.raw(`'${type}' as type`), ) .toString(); }, - selectNotificationMicrosoftTeamsByName: (name: string) => - knex('notification_microsoftteams') - .where('name', '=', name) - .toString(), - selectNotificationRocketChatByName: (name: string) => - knex('notification_rocketchat') - .where('name', '=', name) - .toString(), - selectNotificationSlackByName: (name: string) => - knex('notification_slack') - .where('name', '=', name) - .toString(), - selectNotificationEmailByName: (name: string) => - knex('notification_email') - .where('name', '=', name) - .toString(), - selectNotificationWebhookByName: (name: string) => - knex('notification_webhook') - .where('name', '=', name) - .toString(), - truncateNotificationSlack: () => - knex('notification_slack') - .truncate() - .toString(), - truncateNotificationEmail: () => - knex('notification_email') - .truncate() - .toString(), - truncateNotificationRocketchat: () => - knex('notification_rocketchat') - .truncate() - .toString(), - truncateNotificationMicrosoftTeams: () => - knex('notification_microsoftteams') - .truncate() - .toString(), - truncateNotificationWebhook: () => - knex('notification_webhook') - .truncate() - .toString(), - truncateProjectNotification: () => - knex('project_notification') - .truncate() - .toString() + selectNotificationMicrosoftTeamsByName: (name: string) => knex('notification_microsoftteams') + .where('name', '=', name) + .toString(), + selectNotificationRocketChatByName: (name: string) => knex('notification_rocketchat') + .where('name', '=', name) + .toString(), + selectNotificationSlackByName: (name: string) => knex('notification_slack') + .where('name', '=', name) + .toString(), + selectNotificationEmailByName: (name: string) => knex('notification_email') + .where('name', '=', name) + .toString(), + selectNotificationWebhookByName: (name: string) => knex('notification_webhook') + .where('name', '=', name) + .toString(), + truncateNotificationSlack: () => knex('notification_slack') + .truncate() + .toString(), + truncateNotificationEmail: () => knex('notification_email') + .truncate() + .toString(), + truncateNotificationRocketchat: () => knex('notification_rocketchat') + .truncate() + .toString(), + truncateNotificationMicrosoftTeams: () => knex('notification_microsoftteams') + .truncate() + .toString(), + truncateNotificationWebhook: () => knex('notification_webhook') + .truncate() + .toString(), + truncateProjectNotification: () => knex('project_notification') + .truncate() + .toString(), }; diff --git a/services/api/src/resources/openshift/helpers.ts b/services/api/src/resources/openshift/helpers.ts index 88a671bd33..6a16ee874b 100644 --- a/services/api/src/resources/openshift/helpers.ts +++ b/services/api/src/resources/openshift/helpers.ts @@ -1,54 +1,52 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; -import { asyncPipe } from '@lagoon/commons/dist/util/func'; +import { asyncPipe } from '../../commons/util/func'; import { query } from '../../util/db'; import { Sql } from './sql'; -export const Helpers = (sqlClientPool: Pool) => { - return { - getOpenshiftByOpenshiftInput: async (openshiftInput: { +export const Helpers = (sqlClientPool: Pool) => ({ + getOpenshiftByOpenshiftInput: async (openshiftInput: { id?: number; name?: string; }) => { - const notEmpty = R.complement(R.anyPass([R.isNil, R.isEmpty])); - const hasId = R.both(R.has('id'), R.propSatisfies(notEmpty, 'id')); - const hasName = R.both(R.has('name'), R.propSatisfies(notEmpty, 'name')); - - const openshiftFromId = asyncPipe(R.prop('id'), async id => { - const rows = await query(sqlClientPool, Sql.selectOpenshift(id)); - const openshift = R.prop(0, rows); - - if (!openshift) { - throw new Error('Invalid input for kubernetes'); - } - - return openshift; - }); - - const openshiftFromName = asyncPipe(R.prop('name'), async name => { - const rows = await query( - sqlClientPool, - Sql.selectOpenshiftByName(name) - ); - const openshift = R.prop(0, rows); - - if (!openshift) { - throw new Error('Invalid input for kubernetes'); - } - - return openshift; - }); - - return R.cond([ - [hasId, openshiftFromId], - [hasName, openshiftFromName], - [ - R.T, - () => { - throw new Error('Must provide openshift "id" or "name"'); - } - ] - ])(openshiftInput); - } - }; -}; + const notEmpty = R.complement(R.anyPass([R.isNil, R.isEmpty])); + const hasId = R.both(R.has('id'), R.propSatisfies(notEmpty, 'id')); + const hasName = R.both(R.has('name'), R.propSatisfies(notEmpty, 'name')); + + const openshiftFromId = asyncPipe(R.prop('id'), async id => { + const rows = await query(sqlClientPool, Sql.selectOpenshift(id)); + const openshift = R.prop(0, rows); + + if (!openshift) { + throw new Error('Invalid input for kubernetes'); + } + + return openshift; + }); + + const openshiftFromName = asyncPipe(R.prop('name'), async name => { + const rows = await query( + sqlClientPool, + Sql.selectOpenshiftByName(name), + ); + const openshift = R.prop(0, rows); + + if (!openshift) { + throw new Error('Invalid input for kubernetes'); + } + + return openshift; + }); + + return R.cond([ + [hasId, openshiftFromId], + [hasName, openshiftFromName], + [ + R.T, + () => { + throw new Error('Must provide openshift "id" or "name"'); + }, + ], + ])(openshiftInput); + }, +}); diff --git a/services/api/src/resources/openshift/openshift.test.js b/services/api/src/resources/openshift/openshift.test.js index 51320ba352..7d8cdce79c 100644 --- a/services/api/src/resources/openshift/openshift.test.js +++ b/services/api/src/resources/openshift/openshift.test.js @@ -6,8 +6,8 @@ describe('Sql', () => { const input = { id: 1, patch: { - name: 'test' - } + name: 'test', + }, }; const ret = Sql.updateOpenshift(input); diff --git a/services/api/src/resources/openshift/resolvers.ts b/services/api/src/resources/openshift/resolvers.ts index 05a80520d8..c671cacfe6 100644 --- a/services/api/src/resources/openshift/resolvers.ts +++ b/services/api/src/resources/openshift/resolvers.ts @@ -1,15 +1,15 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { query, isPatchEmpty, knex } from '../../util/db'; import { Helpers as projectHelpers } from '../project/helpers'; import { Sql } from './sql'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; export const getToken: ResolverFn = async ( kubernetes, _args, - { hasPermission, userActivityLogger } + { hasPermission, userActivityLogger }, ) => { try { await hasPermission('openshift', 'view:token'); @@ -21,18 +21,18 @@ export const getToken: ResolverFn = async ( details: kubernetes.name, }, }; - userActivityLogger(`User viewed openshift token`, { + userActivityLogger('User viewed openshift token', { project: '', event: 'api:viewOpenshiftToken', payload: { name: kubernetes.name, id: kubernetes.id, ...auditLog, - } + }, }); return kubernetes.token; - } catch (err) { + } catch (_err) { return null; } }; @@ -40,13 +40,13 @@ export const getToken: ResolverFn = async ( export const getConsoleUrl: ResolverFn = async ( kubernetes, _args, - { hasPermission } + { hasPermission }, ) => { try { await hasPermission('openshift', 'view:token'); return kubernetes.consoleUrl; - } catch (err) { + } catch (_err) { return null; } }; @@ -54,13 +54,13 @@ export const getConsoleUrl: ResolverFn = async ( export const getMonitoringConfig: ResolverFn = async ( kubernetes, _args, - { hasPermission } + { hasPermission }, ) => { try { await hasPermission('openshift', 'view:token'); return kubernetes.monitoringConfig; - } catch (err) { + } catch (_err) { return null; } }; @@ -70,7 +70,7 @@ export const getProjectUser: ResolverFn = async () => null; export const addOpenshift: ResolverFn = async ( args, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('openshift', 'add'); @@ -92,7 +92,7 @@ export const addOpenshift: ResolverFn = async ( name: input.name, id: R.prop(0, rows).id, ...auditLog, - } + }, }); return R.prop(0, rows); @@ -101,16 +101,17 @@ export const addOpenshift: ResolverFn = async ( export const deleteOpenshift: ResolverFn = async ( args, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('openshift', 'delete'); let res = await query(sqlClientPool, knex('project') - .join('openshift', 'project.openshift', '=', 'openshift.id') - .where('openshift.name', input.name).count('project.id', {as: 'numactive'}).toString()); + .join('openshift', 'project.openshift', '=', 'openshift.id') + .where('openshift.name', input.name).count('project.id', { as: 'numactive' }) + .toString()); const numberActiveOs: number = R.path(['0', 'numactive'], res); - if(numberActiveOs > 0) { + if (numberActiveOs > 0) { throw new Error(`Openshift "${input.name} still in use, can not delete`); } @@ -131,7 +132,7 @@ export const deleteOpenshift: ResolverFn = async ( payload: { name: input.name, ...auditLog, - } + }, }); // TODO: maybe check rows for changed result return 'success'; @@ -140,7 +141,7 @@ export const deleteOpenshift: ResolverFn = async ( export const getAllOpenshifts: ResolverFn = async ( root, { disabled, buildImage }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { await hasPermission('openshift', 'viewAll'); @@ -159,13 +160,12 @@ export const getAllOpenshifts: ResolverFn = async ( export const getOpenshiftByProjectId: ResolverFn = async ( { id: pid }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { - // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('openshift', 'view', { - project: pid + project: pid, }); } @@ -177,17 +177,16 @@ export const getOpenshiftByProjectId: ResolverFn = async ( export const getOpenshiftByDeployTargetId: ResolverFn = async ( { id: did }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { // get the project id for the deploytarget - const projectrows = await query(sqlClientPool, Sql.selectProjectIdByDeployTargetId(did) + const projectrows = await query(sqlClientPool, Sql.selectProjectIdByDeployTargetId(did), ); - // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('openshift', 'view', { - project: projectrows[0].project + project: projectrows[0].project, }); } @@ -199,17 +198,17 @@ export const getOpenshiftByDeployTargetId: ResolverFn = async ( export const getOpenshiftByEnvironmentId: ResolverFn = async ( { id: eid }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { // get the project id for the environment const project = await projectHelpers( - sqlClientPool + sqlClientPool, ).getProjectByEnvironmentId(eid); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('openshift', 'view', { - project: project.project + project: project.project, }); } @@ -221,7 +220,7 @@ export const getOpenshiftByEnvironmentId: ResolverFn = async ( export const updateOpenshift: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('openshift', 'update'); @@ -248,7 +247,7 @@ export const updateOpenshift: ResolverFn = async ( name: R.prop(0, rows).name, id: R.prop(0, rows).id, ...auditLog, - } + }, }); return R.prop(0, rows); diff --git a/services/api/src/resources/openshift/sql.ts b/services/api/src/resources/openshift/sql.ts index b49374b255..8960f453e1 100644 --- a/services/api/src/resources/openshift/sql.ts +++ b/services/api/src/resources/openshift/sql.ts @@ -14,7 +14,7 @@ export const Sql = { cloudProvider, cloudRegion, buildImage, - sharedBaasBucketName + sharedBaasBucketName, }: { id?: number; name: string; @@ -29,68 +29,59 @@ export const Sql = { cloudRegion?: string; buildImage?: string; sharedBaasBucketName?: string; - }) => - knex('openshift') - .insert({ - id, - name, - consoleUrl, - token, - routerPattern, - sshHost, - sshPort, - monitoringConfig, - friendlyName, - cloudProvider, - cloudRegion, - buildImage, - sharedBaasBucketName - }) - .toString(), + }) => knex('openshift') + .insert({ + id, + name, + consoleUrl, + token, + routerPattern, + sshHost, + sshPort, + monitoringConfig, + friendlyName, + cloudProvider, + cloudRegion, + buildImage, + sharedBaasBucketName, + }) + .toString(), updateOpenshift: ({ id, - patch + patch, }: { id: number; patch: { [key: string]: any }; - }) => - knex('openshift') - .where('id', '=', id) - .update(patch) - .toString(), - selectOpenshift: (id: number) => - knex('openshift') - .where('id', '=', id) - .toString(), - selectOpenshiftByName: (name: string) => - knex('openshift') - .where('name', '=', name) - .toString(), - truncateOpenshift: () => - knex('openshift') - .truncate() - .toString(), - selectOpenshiftByProjectId: (id: number) => - knex('project AS p') - .select('openshift.*') - .join('openshift', 'openshift.id', '=', 'p.openshift') - .where(knex.raw('p.id = ?', id)) - .toString(), - selectProjectIdByDeployTargetId: (id: number) => - knex('deploy_target_config AS d') - .select('d.project') - .where(knex.raw('d.id = ?', id)) - .toString(), - selectOpenshiftByDeployTargetId: (id: number) => - knex('deploy_target_config AS d') - .select('openshift.*') - .join('openshift', 'openshift.id', '=', 'd.deploy_target') - .where(knex.raw('d.id = ?', id)) - .toString(), - selectOpenshiftByEnvironmentId: (id: number) => - knex('environment AS e') - .select('openshift.*') - .join('openshift', 'openshift.id', '=', 'e.openshift') - .where(knex.raw('e.id = ?', id)) - .toString(), + }) => knex('openshift') + .where('id', '=', id) + .update(patch) + .toString(), + selectOpenshift: (id: number) => knex('openshift') + .where('id', '=', id) + .toString(), + selectOpenshiftByName: (name: string) => knex('openshift') + .where('name', '=', name) + .toString(), + truncateOpenshift: () => knex('openshift') + .truncate() + .toString(), + selectOpenshiftByProjectId: (id: number) => knex('project AS p') + .select('openshift.*') + .join('openshift', 'openshift.id', '=', 'p.openshift') + .where(knex.raw('p.id = ?', id)) + .toString(), + selectProjectIdByDeployTargetId: (id: number) => knex('deploy_target_config AS d') + .select('d.project') + .where(knex.raw('d.id = ?', id)) + .toString(), + selectOpenshiftByDeployTargetId: (id: number) => knex('deploy_target_config AS d') + .select('openshift.*') + .join('openshift', 'openshift.id', '=', 'd.deploy_target') + .where(knex.raw('d.id = ?', id)) + .toString(), + selectOpenshiftByEnvironmentId: (id: number) => knex('environment AS e') + .select('openshift.*') + .join('openshift', 'openshift.id', '=', 'e.openshift') + .where(knex.raw('e.id = ?', id)) + .toString(), }; diff --git a/services/api/src/resources/organization/helpers.ts b/services/api/src/resources/organization/helpers.ts index 99f19e1099..17ac34c2e2 100644 --- a/services/api/src/resources/organization/helpers.ts +++ b/services/api/src/resources/organization/helpers.ts @@ -2,7 +2,7 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; import { query } from '../../util/db'; import { toNumber } from '../../util/func'; -import { asyncPipe } from '@lagoon/commons/dist/util/func'; +import { asyncPipe } from '../../commons/util/func'; import { Sql } from './sql'; export const Helpers = (sqlClientPool: Pool) => { @@ -23,42 +23,42 @@ export const Helpers = (sqlClientPool: Pool) => { return rows; }; const getNotificationsByTypeForOrganizationId = async (id: number, type: string) => { - let input = {id: id, type: type} + const input = { id, type }; const result = await query( sqlClientPool, - Sql.selectNotificationsByTypeByOrganizationId(input) + Sql.selectNotificationsByTypeByOrganizationId(input), ); - return result + return result; }; const getNotificationsForOrganizationId = async (id: number) => { - let input = {id: id, type: "slack"} + const input = { id, type: 'slack' }; // get all the notifications for the projects const slacks = await query( sqlClientPool, - Sql.selectNotificationsByTypeByOrganizationId(input) + Sql.selectNotificationsByTypeByOrganizationId(input), ); - input.type = "rocketchat" + input.type = 'rocketchat'; const rcs = await query( sqlClientPool, - Sql.selectNotificationsByTypeByOrganizationId(input) + Sql.selectNotificationsByTypeByOrganizationId(input), ); - input.type = "microsoftteams" + input.type = 'microsoftteams'; const teams = await query( sqlClientPool, - Sql.selectNotificationsByTypeByOrganizationId(input) + Sql.selectNotificationsByTypeByOrganizationId(input), ); - input.type = "email" + input.type = 'email'; const email = await query( sqlClientPool, - Sql.selectNotificationsByTypeByOrganizationId(input) + Sql.selectNotificationsByTypeByOrganizationId(input), ); - input.type = "webhook" + input.type = 'webhook'; const webhook = await query( sqlClientPool, - Sql.selectNotificationsByTypeByOrganizationId(input) + Sql.selectNotificationsByTypeByOrganizationId(input), ); - const result = [...slacks, ...rcs, ...teams, ...email, ...webhook] - return result + const result = [...slacks, ...rcs, ...teams, ...email, ...webhook]; + return result; }; const getDeployTargetsByOrganizationId = async (id: number) => { const rows = await query(sqlClientPool, Sql.selectOrganizationDeployTargets(id)); @@ -99,20 +99,20 @@ export const Helpers = (sqlClientPool: Pool) => { R.T, () => { throw new Error('Must provide organization "id" or "name"'); - } - ] + }, + ], ])(organizationInput); }, getOrganizationIdByName: async (name: string): Promise => { const idResult = await query( sqlClientPool, - Sql.selectOrganizationByName(name) + Sql.selectOrganizationByName(name), ); const amount = R.length(idResult); if (amount > 1) { throw new Error( - `Multiple organization candidates for '${name}' (${amount} found). Do nothing.` + `Multiple organization candidates for '${name}' (${amount} found). Do nothing.`, ); } @@ -124,5 +124,5 @@ export const Helpers = (sqlClientPool: Pool) => { return toNumber(id); }, - } + }; }; diff --git a/services/api/src/resources/organization/resolvers.ts b/services/api/src/resources/organization/resolvers.ts index 1830fba23e..68e9220b45 100644 --- a/services/api/src/resources/organization/resolvers.ts +++ b/services/api/src/resources/organization/resolvers.ts @@ -1,43 +1,42 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import validator from 'validator'; +import { ResolverFn } from '..'; import { logger } from '../../loggers/logger'; import { query, isPatchEmpty, knex } from '../../util/db'; import { Helpers as projectHelpers } from '../project/helpers'; -import { Helpers} from './helpers'; +import { Helpers } from './helpers'; import { Sql } from './sql'; -import { arrayDiff, toNumber, requestsExtraFields } from '../../util/func'; +import { arrayDiff, requestsExtraFields } from '../../util/func'; import { Helpers as openshiftHelpers } from '../openshift/helpers'; import { Helpers as notificationHelpers } from '../notification/helpers'; import { Helpers as groupHelpers } from '../group/helpers'; -import { AuditType } from '@lagoon/commons/dist/types'; -import validator from 'validator'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; const isValidName = value => { if (validator.matches(value, /[^0-9a-z-]/)) { throw new Error( - 'Only lowercase characters, numbers and dashes allowed for name!' + 'Only lowercase characters, numbers and dashes allowed for name!', ); } if (validator.matches(value, /--/)) { throw new Error('Multiple consecutive dashes are not allowed for name!'); } -} +}; export const addOrganization: ResolverFn = async ( args, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - // check if the name is valid - isValidName(input.name) + isValidName(input.name); try { await hasPermission('organization', 'add'); const org = await query(sqlClientPool, Sql.selectOrganizationByName(input.name)); // if no organization found, create it - if (R.length(org) == 0) { + if (R.length(org) === 0) { const { insertId } = await query(sqlClientPool, Sql.insertOrganization(input)); const rows = await query(sqlClientPool, Sql.selectOrganization(insertId)); @@ -54,50 +53,48 @@ export const addOrganization: ResolverFn = async ( event: 'api:addOrganization', payload: { data: { - input + input, }, ...auditLog, - } + }, }); return R.prop(0, rows); - } else { - throw new Error(`There was an error creating the organization, ${input.name} already exists`); } - } catch (err) { + throw new Error(`There was an error creating the organization, ${input.name} already exists`); + } catch (err) { throw new Error(`There was an error creating the organization ${input.name} ${err}`); } }; - export const addDeployTargetToOrganization: ResolverFn = async ( args, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('organization', 'add'); const org = await query(sqlClientPool, Sql.selectOrganization(input.organization)); - if (R.length(org) == 0) { + if (R.length(org) === 0) { throw new Error( - `Organization doesn't exist` + 'Organization doesn\'t exist', ); } try { - await openshiftHelpers(sqlClientPool).getOpenshiftByOpenshiftInput({id: input.deployTarget}) - } catch (err) { + await openshiftHelpers(sqlClientPool).getOpenshiftByOpenshiftInput({ id: input.deployTarget }); + } catch (err) { throw new Error(`There was an error adding the deployTarget: ${err}`); } - const result = await query(sqlClientPool, Sql.selectDeployTargetsByOrganizationAndDeployTarget(input.organization, input.deployTarget)) + const result = await query(sqlClientPool, Sql.selectDeployTargetsByOrganizationAndDeployTarget(input.organization, input.deployTarget)); if (R.length(result) >= 1) { throw new Error( - `Already added to organization` + 'Already added to organization', ); } try { - await query(sqlClientPool, Sql.addDeployTarget({dtid: input.deployTarget, orgid: input.organization})); - } catch (err) { - throw new Error(`There was an error adding the deployTarget: ${err}`); + await query(sqlClientPool, Sql.addDeployTarget({ dtid: input.deployTarget, orgid: input.organization })); + } catch (err) { + throw new Error(`There was an error adding the deployTarget: ${err}`); } const auditLog: AuditLog = { @@ -117,10 +114,10 @@ export const addDeployTargetToOrganization: ResolverFn = async ( event: 'api:addDeployTargetToOrganization', payload: { data: { - input + input, }, ...auditLog, - } + }, }); return org[0]; @@ -129,20 +126,20 @@ export const addDeployTargetToOrganization: ResolverFn = async ( export const removeDeployTargetFromOrganization: ResolverFn = async ( args, { input }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('organization', 'add'); const org = await query(sqlClientPool, Sql.selectOrganization(input.organization)); - if (R.length(org) == 0) { + if (R.length(org) === 0) { throw new Error( - `Organization doesn't exist` + 'Organization doesn\'t exist', ); } try { await query(sqlClientPool, Sql.removeDeployTarget(input.organization, input.deployTarget)); - } catch (err) { + } catch (err) { throw new Error(`There was an error removing the deployTarget: ${err}`); } @@ -163,20 +160,19 @@ export const removeDeployTargetFromOrganization: ResolverFn = async ( event: 'api:removeDeployTargetFromOrganization', payload: { data: { - input + input, }, ...auditLog, - } + }, }); return org[0]; }; - export const getDeployTargetsByOrganizationId: ResolverFn = async ( { id: oid }, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { // let oid = args.organization; if (args.organization) { @@ -184,13 +180,13 @@ export const getDeployTargetsByOrganizationId: ResolverFn = async ( } await hasPermission('organization', 'view', { - organization: oid, + organization: oid, }); const rows = await query(sqlClientPool, Sql.selectDeployTargetsByOrganization(oid)); if (!rows) { - return null; + return null; } return rows; @@ -199,111 +195,112 @@ export const getDeployTargetsByOrganizationId: ResolverFn = async ( export const getEnvironmentsByOrganizationId: ResolverFn = async ( { id: oid }, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { // let oid = args.organization; if (args.organization) { oid = args.organization; } await hasPermission('organization', 'view', { - organization: oid, + organization: oid, }); const rows = await query(sqlClientPool, Sql.selectOrganizationEnvironments(oid)); if (!rows) { - return null; + return null; } return rows; }; export const updateOrganization: ResolverFn = async ( - root, - { input }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + root, + { input }, + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { + if (input.patch.quotaProject || input.patch.quotaGroup || input.patch.quotaNotification || input.patch.quotaEnvironment || input.patch.quotaRoute) { + await hasPermission('organization', 'update'); + } else { + await hasPermission('organization', 'updateOrganization', input.id); + } - if (input.patch.quotaProject || input.patch.quotaGroup || input.patch.quotaNotification || input.patch.quotaEnvironment || input.patch.quotaRoute) { - await hasPermission('organization', 'update'); - } else { - await hasPermission('organization', 'updateOrganization', input.id); - } - - // if the beta feature flag for api routes is being updated, check if permission to enable this flag is set - // this feature will become generally available in a future version and this flag will not - // be required - if (input.patch.featureApiRoutes) { - if (!adminScopes.platformOwner) { - throw new Error('Setting the api routes feature is only available to platform administrators.'); - } + // if the beta feature flag for api routes is being updated, check if permission to enable this flag is set + // this feature will become generally available in a future version and this flag will not + // be required + if (input.patch.featureApiRoutes) { + if (!adminScopes.platformOwner) { + throw new Error('Setting the api routes feature is only available to platform administrators.'); } + } - if (input.patch.name) { - // check if the name is valid - isValidName(input.patch.name) - } + if (input.patch.name) { + // check if the name is valid + isValidName(input.patch.name); + } - const oid = input.id.toString(); + const oid = input.id.toString(); - if (isPatchEmpty(input)) { - throw new Error('input.patch requires at least 1 attribute'); - } + if (isPatchEmpty(input)) { + throw new Error('input.patch requires at least 1 attribute'); + } - await query(sqlClientPool, Sql.updateOrganization(input)); - const rows = await query(sqlClientPool, Sql.selectOrganization(oid)); + await query(sqlClientPool, Sql.updateOrganization(input)); + const rows = await query(sqlClientPool, Sql.selectOrganization(oid)); - const auditLog: AuditLog = { - resource: { - id: R.prop(0, rows).id.toString(), - type: AuditType.ORGANIZATION, - details: R.prop(0, rows).name, + const auditLog: AuditLog = { + resource: { + id: R.prop(0, rows).id.toString(), + type: AuditType.ORGANIZATION, + details: R.prop(0, rows).name, + }, + organizationId: rows[0].id.toString(), + }; + userActivityLogger(`User updated organization ${R.prop(0, rows).name}`, { + project: '', + organization: input.organization, + event: 'api:updateOrganization', + payload: { + data: { + input, }, - organizationId: rows[0].id.toString(), - }; - userActivityLogger(`User updated organization ${R.prop(0, rows).name}`, { - project: '', - organization: input.organization, - event: 'api:updateOrganization', - payload: { - data: { - input - }, - ...auditLog, - } - }); + ...auditLog, + }, + }); - return R.prop(0, rows); + return R.prop(0, rows); }; export const getOrganizationById: ResolverFn = async ( - id, - args, - { sqlClientPool, hasPermission } + id, + args, + { sqlClientPool, hasPermission }, ) => { - let oid = args.id; - if (id) { - oid = id; - } + let oid = args.id; + if (id) { + oid = id; + } - await hasPermission('organization', 'view', { - organization: oid, - }); + await hasPermission('organization', 'view', { + organization: oid, + }); - const rows = await query(sqlClientPool, Sql.selectOrganization(oid)); - const orgResult = rows[0]; + const rows = await query(sqlClientPool, Sql.selectOrganization(oid)); + const orgResult = rows[0]; - if (!orgResult) { - return null; - } + if (!orgResult) { + return null; + } - return orgResult; + return orgResult; }; export const getOrganizationByName: ResolverFn = async ( name, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { let orgName = args.name; if (name) { @@ -325,32 +322,34 @@ export const getOrganizationByName: ResolverFn = async ( }; export const getAllOrganizations: ResolverFn = async ( - root, - args, - { sqlClientPool, models, hasPermission, keycloakGrant } + root, + args, + { + sqlClientPool, models, hasPermission, keycloakGrant, + }, ) => { - let userOrganizationIds: number[]; - - try { - await hasPermission('organization', 'viewAll'); - } catch (err) { - if (!keycloakGrant) { - logger.warn('No grant available for getAllProjects'); - return []; - } + let userOrganizationIds: number[]; - userOrganizationIds = await models.UserModel.getAllOrganizationIdsForUser({ - id: keycloakGrant.access_token.content.sub - }); + try { + await hasPermission('organization', 'viewAll'); + } catch (_err) { + if (!keycloakGrant) { + logger.warn('No grant available for getAllProjects'); + return []; } - let queryBuilder = knex('organization'); + userOrganizationIds = await models.UserModel.getAllOrganizationIdsForUser({ + id: keycloakGrant.access_token.content.sub, + }); + } + + let queryBuilder = knex('organization'); - if (userOrganizationIds) { - queryBuilder = queryBuilder.whereIn('id', userOrganizationIds); - } - const rows = await query(sqlClientPool, queryBuilder.toString()); - return rows; + if (userOrganizationIds) { + queryBuilder = queryBuilder.whereIn('id', userOrganizationIds); + } + const rows = await query(sqlClientPool, queryBuilder.toString()); + return rows; }; // get projects by organization id, used by organization resolver to list projects @@ -359,11 +358,11 @@ export const getAllOrganizations: ResolverFn = async ( export const getProjectsByOrganizationId: ResolverFn = async ( { id: oid }, args, - { sqlClientPool, hasPermission } + { sqlClientPool }, ) => { const rows = await query( sqlClientPool, - Sql.selectOrganizationProjects(oid) + Sql.selectOrganizationProjects(oid), ); return rows; }; @@ -374,11 +373,10 @@ export const getOrganizationByProject: ResolverFn = async ( project, args, { sqlClientPool, hasPermission }, - info + info, ) => { - if (!project || !project.organization) { - return null + return null; } const extraFields = requestsExtraFields(info, ['id', 'name', 'friendlyName']); @@ -399,25 +397,21 @@ export const getOrganizationByProject: ResolverFn = async ( export const getNotificationsForOrganizationProjectId: ResolverFn = async ( organization, args, - { sqlClientPool, hasPermission } + { sqlClientPool }, ) => { - let oid = args.organization; - if (organization) { - oid = organization.organization; - } let pid = args.organization; if (organization) { pid = organization.id; } - return await notificationHelpers(sqlClientPool).selectNotificationsByProjectId({project: pid}) + return await notificationHelpers(sqlClientPool).selectNotificationsByProjectId({ project: pid }); }; // gets owners of an organization by id export const getOwnersByOrganizationId: ResolverFn = async ( { id: oid }, _input, - { hasPermission, models } + { hasPermission, models }, ) => { await hasPermission('organization', 'view', { organization: oid, @@ -430,13 +424,13 @@ export const getOwnersByOrganizationId: ResolverFn = async ( export const getGroupsByOrganizationId: ResolverFn = async ( { id: oid }, _input, - { hasPermission, models, sqlClientPool } + { hasPermission, models, sqlClientPool }, ) => { await hasPermission('organization', 'viewGroup', { organization: oid, }); - const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, oid) + const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, oid); return orgGroups; }; @@ -445,52 +439,52 @@ export const getGroupsByOrganizationId: ResolverFn = async ( export const getUsersByOrganizationId: ResolverFn = async ( _, args, - { hasPermission, models, sqlClientPool } + { hasPermission, models, sqlClientPool }, ) => { await hasPermission('organization', 'viewUsers', { organization: args.organization, }); - const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, args.organization) + const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, args.organization); - let members = [] + const members = []; for (const group in orgGroups) { const groupMembers = await models.GroupModel.getGroupMembership(orgGroups[group]); // there is probably a better way to do this to only add unique members in the organization in the response let exists = false; for (const member in groupMembers) { for (const m in members) { - if (groupMembers[member].user.id == members[m].id) { - exists = true + if (groupMembers[member].user.id === members[m].id) { + exists = true; } } if (!exists) { // quick check to set the owner flag or not - groupMembers[member].user.owner = false - groupMembers[member].user.admin = false - groupMembers[member].user.comment = null + groupMembers[member].user.owner = false; + groupMembers[member].user.admin = false; + groupMembers[member].user.comment = null; if (groupMembers[member].user.attributes) { - if (groupMembers[member].user.attributes["comment"]) { - groupMembers[member].user.comment = groupMembers[member].user.attributes["comment"][0] + if (groupMembers[member].user.attributes.comment) { + groupMembers[member].user.comment = groupMembers[member].user.attributes.comment[0]; } - if (groupMembers[member].user.attributes["lagoon-organizations"]) { - for (const a in groupMembers[member].user.attributes["lagoon-organizations"]) { - if (parseInt(groupMembers[member].user.attributes["lagoon-organizations"][a]) == args.organization) { - groupMembers[member].user.owner = true + if (groupMembers[member].user.attributes['lagoon-organizations']) { + for (const a in groupMembers[member].user.attributes['lagoon-organizations']) { + if (parseInt(groupMembers[member].user.attributes['lagoon-organizations'][a]) === args.organization) { + groupMembers[member].user.owner = true; } } } - if (groupMembers[member].user.attributes["lagoon-organizations-admin"]) { - for (const a in groupMembers[member].user.attributes["lagoon-organizations-admin"]) { - if (parseInt(groupMembers[member].user.attributes["lagoon-organizations-admin"][a]) == args.organization) { - groupMembers[member].user.admin = true + if (groupMembers[member].user.attributes['lagoon-organizations-admin']) { + for (const a in groupMembers[member].user.attributes['lagoon-organizations-admin']) { + if (parseInt(groupMembers[member].user.attributes['lagoon-organizations-admin'][a]) === args.organization) { + groupMembers[member].user.admin = true; } } } } - members.push(groupMembers[member].user) + members.push(groupMembers[member].user); } - exists = false + exists = false; } } return members.map(row => ({ ...row, organization: args.organization })); @@ -499,92 +493,94 @@ export const getUsersByOrganizationId: ResolverFn = async ( // get a users information for a user in the organization export const getUserByEmailAndOrganizationId: ResolverFn = async ( _root, - { email, organization}, - { sqlClientPool, models, hasPermission }, + { email, organization }, + { models, hasPermission }, ) => { await hasPermission('organization', 'viewUser', { - organization: organization + organization, }); try { const user = await models.UserModel.loadUserByEmail(email); const queryUserGroups = await models.UserModel.getAllGroupsForUser(user.id, organization); - user.owner = false - user.admin = false - user.comment = null - if (user.attributes?.["comment"]) { - user.comment = user.attributes["comment"][0] + user.owner = false; + user.admin = false; + user.comment = null; + if (user.attributes?.comment) { + user.comment = user.attributes.comment[0]; } - if (user.attributes?.["lagoon-organizations"]) { - for (const a in user.attributes["lagoon-organizations"]) { - if (parseInt(user.attributes["lagoon-organizations"][a]) == organization) { - user.owner = true + if (user.attributes?.['lagoon-organizations']) { + for (const a in user.attributes['lagoon-organizations']) { + if (parseInt(user.attributes['lagoon-organizations'][a]) === organization) { + user.owner = true; } } } - if (user.attributes?.["lagoon-organizations-admin"]) { - for (const a in user.attributes["lagoon-organizations-admin"]) { - if (parseInt(user.attributes["lagoon-organizations-admin"][a]) == organization) { - user.admin = true + if (user.attributes?.['lagoon-organizations-admin']) { + for (const a in user.attributes['lagoon-organizations-admin']) { + if (parseInt(user.attributes['lagoon-organizations-admin'][a]) === organization) { + user.admin = true; } } } - if (queryUserGroups.length == 0) { + if (queryUserGroups.length === 0) { // if this user has no groups in this organization, then return nothing about this user at all - return null + return null; } - return { ...user, organization: organization }; - } catch (err) { - return null + return { ...user, organization }; + } catch (_err) { + return null; } - return null + return null; }; // list the group roles for this user that have the organization id -export const getGroupRolesByUserIdAndOrganization: ResolverFn =async ( +export const getGroupRolesByUserIdAndOrganization: ResolverFn = async ( { id: uid, organization }, _input, - { models, sqlClientPool } + { models, sqlClientPool }, ) => { if (organization) { const queryUserGroups = await models.UserModel.getAllGroupsForUser(uid, organization); - let groups = [] + const groups = []; for (const g in queryUserGroups) { - let group = {id: queryUserGroups[g].id, name: queryUserGroups[g].name, role: queryUserGroups[g].subGroups[0].realmRoles[0], groupType: null, organization: null} - if (queryUserGroups[g].attributes?.["type"]) { - group.groupType = queryUserGroups[g].attributes["type"][0] + const group = { + id: queryUserGroups[g].id, name: queryUserGroups[g].name, role: queryUserGroups[g].subGroups[0].realmRoles[0], groupType: null, organization: null, + }; + if (queryUserGroups[g].attributes?.type) { + group.groupType = queryUserGroups[g].attributes.type[0]; } - const org = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(queryUserGroups[g].id) + const org = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(queryUserGroups[g].id); if (org) { - group.organization = org + group.organization = org; } - groups.push(group) + groups.push(group); } return groups; } - return null -} + return null; +}; // list all groups by organization id export const getGroupsByNameAndOrganizationId: ResolverFn = async ( root, { name, organization }, - { hasPermission, models, sqlClientPool } + { hasPermission, models, sqlClientPool }, ) => { try { await hasPermission('organization', 'viewGroup', { - organization: organization, + organization, }); const group = await models.GroupModel.loadGroupByName(name); - const org = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(group.id) - if (org == organization) { - return group + const org = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(group.id); + if (org === organization) { + return group; } - } catch (err) { - return []; + } catch (_err) { + return []; } return []; }; @@ -592,11 +588,11 @@ export const getGroupsByNameAndOrganizationId: ResolverFn = async ( export const getGroupCountByOrganizationProject: ResolverFn = async ( { id: pid }, _input, - { sqlClientPool, models } + { sqlClientPool, models }, ) => { - const orgProjectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) - return orgProjectGroups.length -} + const orgProjectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); + return orgProjectGroups.length; +}; // get the groups of a project in an organization // this is only accessible as a resolver of organizations @@ -605,12 +601,12 @@ export const getGroupCountByOrganizationProject: ResolverFn = async ( export const getGroupsByOrganizationsProject: ResolverFn = async ( { id: pid }, _input, - { sqlClientPool, models } + { sqlClientPool, models }, ) => { // rather than make this more complicated than it needs to be, just return all the groups attached to a project, even those that aren't in the organization // this way the organization owner can make the decision to remove the group from the project // this isn't an impossibile situation, but is unlikely, due to the historical nature of lagoons group attachment process - const orgProjectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) + const orgProjectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); return orgProjectGroups; }; @@ -618,79 +614,79 @@ export const getGroupsByOrganizationsProject: ResolverFn = async ( // this will return errors if there are projects or groups that are part of different organizations // this is a helper function that is a WIP, not fully flushed out -const checkProjectGroupAssociation = async (oid, projectGroups, projectGroupNames, otherOrgs, groupProjectIds,projectInOtherOrgs, sqlClientPool) => { - // get all the groups the requested project is in - for (const group of projectGroups) { - // for each group the project is in, check if it has an organization +const checkProjectGroupAssociation = async (oid, projectGroups, projectGroupNames, otherOrgs, groupProjectIds, projectInOtherOrgs, sqlClientPool) => { + // get all the groups the requested project is in + for (const group of projectGroups) { + // for each group the project is in, check if it has an organization - const groupOrganization = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(group.id); - // if it has an organization that is not the requested organization, add it to a list - if (groupOrganization != null && groupOrganization != oid) { - projectGroupNames.push({group: group.name, organization: groupOrganization.toString()}) - otherOrgs.push(groupOrganization.toString()) - } - // for each group the project is in, get the list of projects that are also in this group - const groupProjects = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(group.id); - for (const project of groupProjects) { - groupProjectIds.push({group: group.name, project: project}) - } + const groupOrganization = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(group.id); + // if it has an organization that is not the requested organization, add it to a list + if (groupOrganization != null && groupOrganization !== oid) { + projectGroupNames.push({ group: group.name, organization: groupOrganization.toString() }); + otherOrgs.push(groupOrganization.toString()); } + // for each group the project is in, get the list of projects that are also in this group + const groupProjects = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(group.id); + for (const project of groupProjects) { + groupProjectIds.push({ group: group.name, project }); + } + } - if (groupProjectIds.length > 0) { - // for all the groups->projects associations - for (const pGroup of groupProjectIds) { - const project = await projectHelpers(sqlClientPool).getProjectById(pGroup.project) - // check if the projects are in an organization, and if so, add it to a list if it is in one not in the requested organization - if (project.organization != oid && project.organization != null) { - projectInOtherOrgs.push({group: pGroup.group, project: project.name, organization: project.organization}) - otherOrgs.push(project.organization.toString()) - } + if (groupProjectIds.length > 0) { + // for all the groups->projects associations + for (const pGroup of groupProjectIds) { + const project = await projectHelpers(sqlClientPool).getProjectById(pGroup.project); + // check if the projects are in an organization, and if so, add it to a list if it is in one not in the requested organization + if (project.organization !== oid && project.organization != null) { + projectInOtherOrgs.push({ group: pGroup.group, project: project.name, organization: project.organization }); + otherOrgs.push(project.organization.toString()); } } + } - // report errors here - const uniqOtherOrgs = new Set(otherOrgs) - if (projectInOtherOrgs.length > 0) { - if (uniqOtherOrgs.size > 1) { - throw new Error(`This project has groups that have projects in other organizations: [${JSON.stringify(projectInOtherOrgs)}]`) - } else { - // if there is only 1 organization across all the associated projects/groups, then its possible to modify all of these to change them - // to the new organization - // throw new Error(`This project has groups that have projects in 1 other organizations: [${[...uniqOtherOrgs]}]`) - } + // report errors here + const uniqOtherOrgs = new Set(otherOrgs); + if (projectInOtherOrgs.length > 0) { + if (uniqOtherOrgs.size > 1) { + throw new Error(`This project has groups that have projects in other organizations: [${JSON.stringify(projectInOtherOrgs)}]`); + } else { + // if there is only 1 organization across all the associated projects/groups, then its possible to modify all of these to change them + // to the new organization + // throw new Error(`This project has groups that have projects in 1 other organizations: [${[...uniqOtherOrgs]}]`) } - if (projectGroupNames.length > 0) { - if (uniqOtherOrgs.size > 1) { - throw new Error(`This project has groups that are in other organizations: [${JSON.stringify(projectGroupNames)}]`) - } else { - // if there is only 1 organization across all the associated projects/groups, then its possible to modify all of these to change them - // to the new organization - // throw new Error(`This project has groups that are in 1 other organizations: [${[...uniqOtherOrgs]}]`) - } + } + if (projectGroupNames.length > 0) { + if (uniqOtherOrgs.size > 1) { + throw new Error(`This project has groups that are in other organizations: [${JSON.stringify(projectGroupNames)}]`); + } else { + // if there is only 1 organization across all the associated projects/groups, then its possible to modify all of these to change them + // to the new organization + // throw new Error(`This project has groups that are in 1 other organizations: [${[...uniqOtherOrgs]}]`) } -} + } +}; export const getProjectGroupOrganizationAssociation: ResolverFn = async ( _root, { input }, - { sqlClientPool, models, hasPermission } + { sqlClientPool, models, hasPermission }, ) => { - let pid = input.project; - let oid = input.organization; + const pid = input.project; + const oid = input.organization; // platform admin only as it potentially reveals information about projects/orgs/groups await hasPermission('organization', 'add'); - const groupProjectIds = [] - const projectInOtherOrgs = [] - const projectGroupNames = [] - const otherOrgs = [] + const groupProjectIds = []; + const projectInOtherOrgs = []; + const projectGroupNames = []; + const otherOrgs = []; // get all the groups the requested project is in - const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) - await checkProjectGroupAssociation(oid, projectGroups, projectGroupNames, otherOrgs, groupProjectIds, projectInOtherOrgs, sqlClientPool) + const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); + await checkProjectGroupAssociation(oid, projectGroups, projectGroupNames, otherOrgs, groupProjectIds, projectInOtherOrgs, sqlClientPool); - return "success"; + return 'success'; }; // remove project from an organization @@ -699,48 +695,50 @@ export const getProjectGroupOrganizationAssociation: ResolverFn = async ( export const removeProjectFromOrganization: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, models, userActivityLogger } + { + sqlClientPool, hasPermission, models, userActivityLogger, + }, ) => { // platform admin only await hasPermission('organization', 'add'); - let pid = input.project; - const project = await projectHelpers(sqlClientPool).getProjectById(pid) - if (project.organization != input.organization) { + const pid = input.project; + const project = await projectHelpers(sqlClientPool).getProjectById(pid); + if (project.organization !== input.organization) { throw new Error( - `Project is not in organization` + 'Project is not in organization', ); } try { - const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) + const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); - let removeGroups = [] + const removeGroups = []; for (const g in projectGroups) { - if (projectGroups[g].attributes["type"] == "project-default-group") { + if (projectGroups[g].attributes.type === 'project-default-group') { // remove all users from the project default group except the `default-user@project` - await models.GroupModel.removeNonProjectDefaultUsersFromGroup(projectGroups[g], project.name) + await models.GroupModel.removeNonProjectDefaultUsersFromGroup(projectGroups[g], project.name); // remove the default project group from the group_organization association table // when the project is removed from the organization await models.GroupModel.removeGroupFromOrganization(projectGroups[g].id); } else { - removeGroups.push(projectGroups[g]) + removeGroups.push(projectGroups[g]); } } // remove groups from project await models.GroupModel.removeProjectFromGroups(pid, removeGroups); - } catch (err) { + } catch (_err) { throw new Error( - `Unable to remove all groups from the project` - ) + 'Unable to remove all groups from the project', + ); } try { // remove all notifications from project - await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({project: pid}) - } catch (err) { + await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({ project: pid }); + } catch (_err) { throw new Error( - `Unable to remove all notifications from the project` - ) + 'Unable to remove all notifications from the project', + ); } try { @@ -749,15 +747,15 @@ export const removeProjectFromOrganization: ResolverFn = async ( sqlClientPool, Sql.updateProjectOrganization({ pid, - patch:{ + patch: { organization: null, - } - }) + }, + }), ); - } catch (err) { + } catch (_err) { throw new Error( - `Unable to remove project from organization` - ) + 'Unable to remove project from organization', + ); } const org = await query(sqlClientPool, Sql.selectOrganization(input.organization)); @@ -780,14 +778,14 @@ export const removeProjectFromOrganization: ResolverFn = async ( event: 'api:removeProjectFromOrganization', payload: { data: { - input + input, }, ...auditLog, - } + }, }); return projectHelpers(sqlClientPool).getProjectById(pid); -} +}; // add existing project to an organization // this removes all notifications and groups from the project and resets all the access to the project to only @@ -795,28 +793,29 @@ export const removeProjectFromOrganization: ResolverFn = async ( export const addExistingProjectToOrganization: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, userActivityLogger, models } + { + sqlClientPool, hasPermission, userActivityLogger, models, + }, ) => { - // platform admin only as it potentially reveals information about projects/orgs/groups await hasPermission('organization', 'add'); const rows = await query(sqlClientPool, Sql.selectOrganization(input.organization)); const orgResult = rows[0]; - let pid = input.project; - let oid = input.organization; - const project = await projectHelpers(sqlClientPool).getProjectById(pid) + const pid = input.project; + const oid = input.organization; + const project = await projectHelpers(sqlClientPool).getProjectById(pid); // get all the groups the requested project is in try { - const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) + const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); - let removeGroups = [] + const removeGroups = []; for (const g in projectGroups) { - if (projectGroups[g].attributes["type"] == "project-default-group") { + if (projectGroups[g].attributes.type === 'project-default-group') { // remove all users from the project default group except the `default-user@project` - await models.GroupModel.removeNonProjectDefaultUsersFromGroup(projectGroups[g], project.name) + await models.GroupModel.removeNonProjectDefaultUsersFromGroup(projectGroups[g], project.name); // update group await models.GroupModel.updateGroup({ id: projectGroups[g].id, @@ -824,33 +823,33 @@ export const addExistingProjectToOrganization: ResolverFn = async ( attributes: { ...projectGroups[g].attributes, // lagoon-organization attribute is removed for legacy reasons only, theses values are stored in the api-db now - "lagoon-organization": [""] - } + 'lagoon-organization': [''], + }, }); // remove the default project group from the group_organization association table // when the project is about to be added to the new organization - await groupHelpers(sqlClientPool).removeGroupFromOrganization(projectGroups[g].id) + await groupHelpers(sqlClientPool).removeGroupFromOrganization(projectGroups[g].id); } else { - removeGroups.push(projectGroups[g]) + removeGroups.push(projectGroups[g]); } } // remove groups from project await models.GroupModel.removeProjectFromGroups(pid, removeGroups); - } catch (err) { + } catch (_err) { throw new Error( - `Unable to remove all groups from the project` - ) + 'Unable to remove all groups from the project', + ); } try { // remove all notifications from project - await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({project: pid}) - } catch (err) { + await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({ project: pid }); + } catch (_err) { throw new Error( - `Unable to remove all notifications from the project` - ) + 'Unable to remove all notifications from the project', + ); } - const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) + const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); // check if project.organization is already set? // get all groups the project is in for (const group of projectGroups) { @@ -861,10 +860,10 @@ export const addExistingProjectToOrganization: ResolverFn = async ( attributes: { ...group.attributes, // lagoon-organization attribute is added for legacy reasons only, theses values are stored in the api-db now - "lagoon-organization": [input.organization] - } + 'lagoon-organization': [input.organization], + }, }); - await groupHelpers(sqlClientPool).addOrganizationToGroup(input.organization, group.id) + await groupHelpers(sqlClientPool).addOrganizationToGroup(input.organization, group.id); // log this activity @@ -880,16 +879,16 @@ export const addExistingProjectToOrganization: ResolverFn = async ( details: group.name, }, }; - userActivityLogger(`User added a group to organization`, { + userActivityLogger('User added a group to organization', { project: '', organization: input.organization, event: 'api:updateOrganizationGroup', payload: { data: { - updatedGroup + updatedGroup, }, ...auditLog, - } + }, }); } @@ -900,10 +899,10 @@ export const addExistingProjectToOrganization: ResolverFn = async ( sqlClientPool, Sql.updateProjectOrganization({ pid, - patch:{ + patch: { organization: oid, - } - }) + }, + }), ); // log this activity @@ -920,69 +919,67 @@ export const addExistingProjectToOrganization: ResolverFn = async ( details: project.name, }, }; - userActivityLogger(`User added an existing project to organization`, { + userActivityLogger('User added an existing project to organization', { project: '', organization: input.organization, event: 'api:addExistingProjectToOrganization', payload: { data: { project: pid, - patch:{ + patch: { organization: oid, - } + }, }, ...auditLog, - } + }, }); return projectHelpers(sqlClientPool).getProjectById(pid); -} +}; const checkOrgProjectGroup = async (sqlClientPool, input, models) => { // check the organization exists const organizationData = await Helpers(sqlClientPool).getOrganizationById(input.organization); if (organizationData === undefined) { - throw new Error(`Organization does not exist`) + throw new Error('Organization does not exist'); } // check the requested group exists const group = await models.GroupModel.loadGroupByIdOrName(input); if (group === undefined) { - throw new Error(`Group does not exist`) + throw new Error('Group does not exist'); } // check the organization for projects currently attached to it const projectsByOrg = await projectHelpers(sqlClientPool).getProjectByOrganizationId(input.organization); - const projectIdsByOrg = [] + const projectIdsByOrg = []; for (const project of projectsByOrg) { - projectIdsByOrg.push(parseInt(project.id)) + projectIdsByOrg.push(parseInt(project.id)); } // get the project ids - const groupProjectIds = [] + const groupProjectIds = []; const groupProjects = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(group.id); if (groupProjects.length > 0) { for (const project of groupProjects) { - groupProjectIds.push(project) + groupProjectIds.push(project); } } if (projectIdsByOrg.length > 0 && groupProjectIds.length > 0) { - if (projectIdsByOrg.length == 0) { - let filters = arrayDiff(groupProjectIds, projectIdsByOrg) - throw new Error(`This organization has no projects associated to it, the following projects that are not part of the requested organization: [${filters}]`) - } else { - if (groupProjectIds.length > 0) { - let filters = arrayDiff(groupProjectIds, projectIdsByOrg) - if (filters.length > 0) { - throw new Error(`This group has the following projects that are not part of the requested organization: [${filters}]`) - } + if (projectIdsByOrg.length === 0) { + const filters = arrayDiff(groupProjectIds, projectIdsByOrg); + throw new Error(`This organization has no projects associated to it, the following projects that are not part of the requested organization: [${filters}]`); + } else if (groupProjectIds.length > 0) { + const filters = arrayDiff(groupProjectIds, projectIdsByOrg); + if (filters.length > 0) { + throw new Error(`This group has the following projects that are not part of the requested organization: [${filters}]`); } } } - return group -} + return group; +}; // check an existing group to see if it can be added to an organization // this function will return errors if there are projects in the group that are not in the organization @@ -991,14 +988,14 @@ const checkOrgProjectGroup = async (sqlClientPool, input, models) => { export const getGroupProjectOrganizationAssociation: ResolverFn = async ( _root, { input }, - { models, sqlClientPool, hasPermission } + { models, sqlClientPool, hasPermission }, ) => { // platform admin only as it potentially reveals information about projects/orgs/groups await hasPermission('organization', 'add'); - await checkOrgProjectGroup(sqlClientPool, input, models) + await checkOrgProjectGroup(sqlClientPool, input, models); - return "success" + return 'success'; }; // add an existing group to an organization @@ -1007,7 +1004,9 @@ export const getGroupProjectOrganizationAssociation: ResolverFn = async ( export const addExistingGroupToOrganization: ResolverFn = async ( _root, { input }, - { models, sqlClientPool, hasPermission, userActivityLogger } + { + models, sqlClientPool, hasPermission, userActivityLogger, + }, ) => { // platform admin only as it potentially reveals information about projects/orgs/groups await hasPermission('organization', 'add'); @@ -1015,10 +1014,10 @@ export const addExistingGroupToOrganization: ResolverFn = async ( // check the organization exists const organizationData = await Helpers(sqlClientPool).getOrganizationById(input.organization); if (organizationData === undefined) { - throw new Error(`Organization does not exist`) + throw new Error('Organization does not exist'); } - const group = await checkOrgProjectGroup(sqlClientPool, input, models) + const group = await checkOrgProjectGroup(sqlClientPool, input, models); // update the group to be in the organization const updatedGroup = await models.GroupModel.updateGroup({ @@ -1027,10 +1026,10 @@ export const addExistingGroupToOrganization: ResolverFn = async ( attributes: { ...group.attributes, // lagoon-organization attribute is added for legacy reasons only, theses values are stored in the api-db now - "lagoon-organization": [input.organization] - } + 'lagoon-organization': [input.organization], + }, }); - await groupHelpers(sqlClientPool).addOrganizationToGroup(input.organization, group.id) + await groupHelpers(sqlClientPool).addOrganizationToGroup(input.organization, group.id); // log this activity @@ -1046,55 +1045,56 @@ export const addExistingGroupToOrganization: ResolverFn = async ( details: group.name, }, }; - userActivityLogger(`User added a group to organization`, { + userActivityLogger('User added a group to organization', { project: '', organization: input.organization, event: 'api:updateOrganizationGroup', payload: { data: { - updatedGroup + updatedGroup, }, ...auditLog, - } + }, }); - return updatedGroup + return updatedGroup; }; // removes a user from all groups in an organisation export const removeUserFromOrganizationGroups: ResolverFn = async ( _root, { input: { user: userInput, organization: organizationInput } }, - { models, sqlClientPool, hasPermission, userActivityLogger } + { + models, sqlClientPool, hasPermission, userActivityLogger, + }, ) => { - if (R.isEmpty(userInput)) { throw new Error('You must provide a user id or email'); } const user = await models.UserModel.loadUserByIdOrEmail({ id: R.prop('id', userInput), - email: R.prop('email', userInput) + email: R.prop('email', userInput), }); // check the organization exists const organizationData = await Helpers(sqlClientPool).getOrganizationById(organizationInput); if (organizationData === undefined) { - throw new Error(`Organization does not exist`) + throw new Error('Organization does not exist'); } // check permissions and get groups await hasPermission('organization', 'removeGroup', { organization: organizationInput, }); - const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, organizationInput) + const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, organizationInput); // iterate through groups and remove the user - let groupsRemoved = [] + const groupsRemoved = []; for (const group in orgGroups) { // if the groups organization is the one to remove from, push it to a new array - const org = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(orgGroups[group].id) - if (org == organizationInput) { + const org = await groupHelpers(sqlClientPool).selectOrganizationIdByGroupId(orgGroups[group].id); + if (org === organizationInput) { groupsRemoved.push(orgGroups[group]); } } @@ -1102,7 +1102,7 @@ export const removeUserFromOrganizationGroups: ResolverFn = async ( try { await models.GroupModel.removeUserFromGroups(user, groupsRemoved); } catch (error) { - throw new Error(`Unable to remove user from groups: ${error}`) + throw new Error(`Unable to remove user from groups: ${error}`); } const auditLog: AuditLog = { @@ -1113,7 +1113,7 @@ export const removeUserFromOrganizationGroups: ResolverFn = async ( }, linkedResource: { type: AuditType.GROUP, - details: "multiple groups", + details: 'multiple groups', }, }; userActivityLogger(`User removed from these groups in organization: ${organizationData.name}`, { @@ -1122,11 +1122,11 @@ export const removeUserFromOrganizationGroups: ResolverFn = async ( event: 'api:removeUserFromOrganizationGroups', payload: { input: { - user: userInput, organization: organizationInput + user: userInput, organization: organizationInput, }, data: groupsRemoved, ...auditLog, - } + }, }); return organizationData; @@ -1136,61 +1136,63 @@ export const removeUserFromOrganizationGroups: ResolverFn = async ( export const deleteOrganization: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger, models } + { + sqlClientPool, hasPermission, userActivityLogger, models, + }, ) => { await hasPermission('organization', 'delete', { - organization: input.id + organization: input.id, }); const rows = await query(sqlClientPool, Sql.selectOrganization(input.id)); - if (R.length(rows) == 0) { + if (R.length(rows) === 0) { throw new Error( - `Organization doesn't exist` + 'Organization doesn\'t exist', ); } const orgResult = rows[0]; const projects = await query( - sqlClientPool, Sql.selectOrganizationProjects(orgResult.id) + sqlClientPool, Sql.selectOrganizationProjects(orgResult.id), ); if (projects.length > 0) { // throw error if there are any existing environments throw new Error( - 'Unable to delete organization, there are existing projects that need to be removed first' + 'Unable to delete organization, there are existing projects that need to be removed first', ); } - const notifications = await Helpers(sqlClientPool).getNotificationsForOrganizationId(orgResult.id) + const notifications = await Helpers(sqlClientPool).getNotificationsForOrganizationId(orgResult.id); if (notifications.length > 0) { // throw error if there are any existing environments throw new Error( - 'Unable to delete organization, there are existing notifications that need to be removed first' + 'Unable to delete organization, there are existing notifications that need to be removed first', ); } - const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, orgResult.id) + const orgGroups = await groupHelpers(sqlClientPool).selectGroupsByOrganizationId(models, orgResult.id); if (orgGroups.length > 0) { // throw error if there are any existing environments throw new Error( - 'Unable to delete organization, there are existing groups that need to be removed first' + 'Unable to delete organization, there are existing groups that need to be removed first', ); } try { await query( sqlClientPool, - Sql.deleteOrganizationDeployTargets(orgResult.id) + Sql.deleteOrganizationDeployTargets(orgResult.id), ); await query( sqlClientPool, - Sql.deleteOrganization(orgResult.id) + Sql.deleteOrganization(orgResult.id), ); - } catch (err) { + } catch (_err) { throw new Error( - `Unable to delete organization` - ) + 'Unable to delete organization', + ); } const auditLog: AuditLog = { @@ -1205,131 +1207,133 @@ export const deleteOrganization: ResolverFn = async ( event: 'api:deleteOrganization', payload: { input: { - orgResult + orgResult, }, ...auditLog, - } + }, }); return 'success'; }; // recursive check of a project and the groups of that project const checkProjectGroups = async (groupProjectIds, projectIds, projectsGroups, models, sqlClientPool, pid) => { - const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) - let index = projectIds.findIndex((item) => item === pid); + const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); + const index = projectIds.findIndex((item) => item === pid); if (index === -1) { - projectIds.push(pid) + projectIds.push(pid); // get all the groups the requested project is in for (const group of projectGroups) { - const groupProjectIdss = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(group.id) + const groupProjectIdss = await groupHelpers(sqlClientPool).selectProjectIdsByGroupID(group.id); // for each group the project is in, get the list of projects that are also in this group for (const project of groupProjectIdss) { - let index = groupProjectIds.findIndex((item) => item.group === group.name); + const index = groupProjectIds.findIndex((item) => item.group === group.name); if (index === -1) { - groupProjectIds.push({group: group.name, project: project}) - projectsGroups.push(group) + groupProjectIds.push({ group: group.name, project }); + projectsGroups.push(group); } // recurse the project if it hasn't already had been done - let index2 = projectIds.findIndex((item) => item === project); + const index2 = projectIds.findIndex((item) => item === project); if (index2 === -1) { - await checkProjectGroups(groupProjectIds, projectIds, projectsGroups, models, sqlClientPool, project) + await checkProjectGroups(groupProjectIds, projectIds, projectsGroups, models, sqlClientPool, project); } } } } -} +}; const checkBulkProjectGroupAssociation = async (oid, pid, projectsToMove, groupsToMove, projectsInOtherOrgs, groupsInOtherOrgs, sqlClientPool, models) => { const groupProjectIds = []; const projectIds = []; - const projectsGroups = [] - await checkProjectGroups(groupProjectIds, projectIds, projectsGroups, models, sqlClientPool, pid) + const projectsGroups = []; + await checkProjectGroups(groupProjectIds, projectIds, projectsGroups, models, sqlClientPool, pid); // for all the projects in the first projects group, iterate through the projects and the groups attached // to these projects and try to build out a map of all the groups and projects that are linked by the primary project if (groupProjectIds.length > 0) { for (const pGroup of groupProjectIds) { - const project = await projectHelpers(sqlClientPool).getProjectById(pGroup.project) + const project = await projectHelpers(sqlClientPool).getProjectById(pGroup.project); // check if the project is already in the requested organization - if (project.organization != oid && project.organization == null) { - let alreadyAdded = false + if (project.organization !== oid && project.organization == null) { + let alreadyAdded = false; for (const f of projectsToMove) { - if (f.id == project.id) { - alreadyAdded = true + if (f.id === project.id) { + alreadyAdded = true; } } if (!alreadyAdded) { // if it isn't already in the requested organization, add it to the list of projects that should be moved - projectsToMove.push(project) + projectsToMove.push(project); } } else { // if the project is in a completely different organization - if (project.organization != oid) { - let alreadyAdded = false + if (project.organization !== oid) { + let alreadyAdded = false; for (const f of projectsInOtherOrgs) { - if (f.id == project.id) { - alreadyAdded = true + if (f.id === project.id) { + alreadyAdded = true; } } if (!alreadyAdded) { // add it to the lsit of projects that will cause this check to fail - projectsInOtherOrgs.push(project) + projectsInOtherOrgs.push(project); } } } } for (const group of projectsGroups) { // for every group that the project is in, check if the group is already in the requested organization - if (group.organization != oid && group.organization == null) { - let alreadyAdded = false + if (group.organization !== oid && group.organization == null) { + let alreadyAdded = false; for (const f of groupsToMove) { - if (f.id == group.id) { - alreadyAdded = true + if (f.id === group.id) { + alreadyAdded = true; } } if (!alreadyAdded) { // if it isn't already in the requested organization, add it to the list of groups that should be moved - groupsToMove.push(group) + groupsToMove.push(group); } } else { // if the group is in a completely different organization - if (group.organization != oid) { - let alreadyAdded = false + if (group.organization !== oid) { + let alreadyAdded = false; for (const f of groupsInOtherOrgs) { - if (f.id == group.id) { - alreadyAdded = true + if (f.id === group.id) { + alreadyAdded = true; } } if (!alreadyAdded) { // add it to the lsit of projects that will cause this check to fail - groupsInOtherOrgs.push(group) + groupsInOtherOrgs.push(group); } } } } } -} +}; export const checkBulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( _root, { input }, - { sqlClientPool, models, hasPermission } + { sqlClientPool, models, hasPermission }, ) => { - let pid = input.project; - let oid = input.organization; + const pid = input.project; + const oid = input.organization; // platform admin only as it potentially reveals information about projects/orgs/groups await hasPermission('organization', 'add'); - const projectsToMove = [] - const groupsToMove = [] - const projectsInOtherOrgs = [] - const groupsInOtherOrgs = [] + const projectsToMove = []; + const groupsToMove = []; + const projectsInOtherOrgs = []; + const groupsInOtherOrgs = []; // get all the groups the requested project is in - await checkBulkProjectGroupAssociation(oid, pid, projectsToMove, groupsToMove, projectsInOtherOrgs, groupsInOtherOrgs, sqlClientPool, models) + await checkBulkProjectGroupAssociation(oid, pid, projectsToMove, groupsToMove, projectsInOtherOrgs, groupsInOtherOrgs, sqlClientPool, models); - return { projects: projectsToMove, groups: groupsToMove, otherOrgProjects: projectsInOtherOrgs, otherOrgGroups: groupsInOtherOrgs }; + return { + projects: projectsToMove, groups: groupsToMove, otherOrgProjects: projectsInOtherOrgs, otherOrgGroups: groupsInOtherOrgs, + }; }; // given a project, collect all the groups that this project has, and all the projects that those groups have and their associated projects @@ -1337,11 +1341,12 @@ export const checkBulkImportProjectsAndGroupsToOrganization: ResolverFn = async export const bulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( root, { input, detachNotifications }, - { sqlClientPool, hasPermission, userActivityLogger, models } + { + sqlClientPool, hasPermission, userActivityLogger, models, + }, ) => { - - let pid = input.project; - let oid = input.organization; + const pid = input.project; + const oid = input.organization; // platform admin only as it potentially reveals information about projects/orgs/groups await hasPermission('organization', 'add'); @@ -1349,20 +1354,20 @@ export const bulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( const rows = await query(sqlClientPool, Sql.selectOrganization(input.organization)); const orgResult = rows[0]; - const projectsToMove = [] - const groupsToMove = [] - const projectsInOtherOrgs = [] - const groupsInOtherOrgs = [] + const projectsToMove = []; + const groupsToMove = []; + const projectsInOtherOrgs = []; + const groupsInOtherOrgs = []; // get all the groups the requested project is in - await checkBulkProjectGroupAssociation(oid, pid, projectsToMove, groupsToMove, projectsInOtherOrgs, groupsInOtherOrgs, sqlClientPool, models) + await checkBulkProjectGroupAssociation(oid, pid, projectsToMove, groupsToMove, projectsInOtherOrgs, groupsInOtherOrgs, sqlClientPool, models); // if anything comes back in projectsInOtherOrgs or groupsInOtherOrgs, then this mutation should fail and inform the user // to run the query first and return the fields that contain information about why it can't move the projects if (projectsInOtherOrgs.length > 0 || groupsInOtherOrgs.length > 0) { throw new Error( - `The process detected projects or groups that are in another organization already, you should run checkBulkImportProjectsAndGroupsToOrganization and return otherOrgProjects and otherOrgGroups fields` - ) + 'The process detected projects or groups that are in another organization already, you should run checkBulkImportProjectsAndGroupsToOrganization and return otherOrgProjects and otherOrgGroups fields', + ); } // update all projects to be in the organization @@ -1371,20 +1376,20 @@ export const bulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( for (const group of groupsToMove) { // update the groups of the project to be in the organization if (!groupsDone.includes(group.id)) { - if (group.organization != oid && group.organization == null) { + if (group.organization !== oid && group.organization == null) { await models.GroupModel.updateGroup({ id: group.id, name: group.name, attributes: { ...group.attributes, // lagoon-organization attribute is added for legacy reasons only, theses values are stored in the api-db now - "lagoon-organization": [input.organization] - } + 'lagoon-organization': [input.organization], + }, }); if (input.organization) { - await groupHelpers(sqlClientPool).addOrganizationToGroup(input.organization, group.id) + await groupHelpers(sqlClientPool).addOrganizationToGroup(input.organization, group.id); } - groupsDone.push(group.id) + groupsDone.push(group.id); // log this activity @@ -1400,28 +1405,28 @@ export const bulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( details: group.name, }, }; - userActivityLogger(`User added a group to organization`, { + userActivityLogger('User added a group to organization', { project: '', organization: input.organization, event: 'api:addGroupToOrganization', payload: { data: { group: group.name, - organization: oid + organization: oid, }, ...auditLog, - } + }, }); } } } for (const project of projectsToMove) { if (!projectsDone.includes(project.id)) { - if (project.organization != oid && project.organization == null) { + if (project.organization !== oid && project.organization == null) { if (detachNotifications) { // remove all notifications from projects before adding them to the organizations try { - await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({project: project.id}) + await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({ project: project.id }); const auditLog: AuditLog = { resource: { id: orgResult.id.toString(), @@ -1430,27 +1435,27 @@ export const bulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( }, linkedResource: { type: AuditType.NOTIFICATION, - details: "multiple notifications", + details: 'multiple notifications', }, }; - userActivityLogger(`User removed all notifications from project`, { + userActivityLogger('User removed all notifications from project', { project: '', organization: input.organization, event: 'api:removeNotificationsFromProject', payload: { data: { project: project.id, - patch:{ + patch: { organization: oid, }, ...auditLog, - } - } + }, + }, }); - } catch (err) { + } catch (_err) { throw new Error( - `Unable to remove all notifications from the project` - ) + 'Unable to remove all notifications from the project', + ); } } @@ -1459,12 +1464,12 @@ export const bulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( sqlClientPool, Sql.updateProjectOrganization({ pid: project.id, - patch:{ + patch: { organization: oid, - } - }) + }, + }), ); - projectsDone.push(project.id) + projectsDone.push(project.id); // log this activity const auditLog: AuditLog = { @@ -1479,23 +1484,25 @@ export const bulkImportProjectsAndGroupsToOrganization: ResolverFn = async ( details: project.name, }, }; - userActivityLogger(`User added a project to organization`, { + userActivityLogger('User added a project to organization', { project: '', organization: input.organization, event: 'api:addExistingProjectToOrganization', payload: { data: { project: project.id, - patch:{ + patch: { organization: oid, }, ...auditLog, - } - } + }, + }, }); } } } - return { projects: projectsToMove, groups: groupsToMove, otherOrgProjects: projectsInOtherOrgs, otherOrgGroups: groupsInOtherOrgs }; -} + return { + projects: projectsToMove, groups: groupsToMove, otherOrgProjects: projectsInOtherOrgs, otherOrgGroups: groupsInOtherOrgs, + }; +}; diff --git a/services/api/src/resources/organization/sql.ts b/services/api/src/resources/organization/sql.ts index 5a24d62283..6a8c8a5241 100644 --- a/services/api/src/resources/organization/sql.ts +++ b/services/api/src/resources/organization/sql.ts @@ -5,12 +5,12 @@ export const Sql = { id, name, friendlyName = name, // default to name if not provided - description = "", + description = '', quotaProject, quotaGroup, quotaNotification, quotaEnvironment, - quotaRoute + quotaRoute, }: { id?: number; name: string; @@ -21,81 +21,72 @@ export const Sql = { quotaNotification?: number; quotaEnvironment?: number; quotaRoute?: number; - }) => - knex('organization') - .insert({ - id, - name, - friendlyName, - description, - quotaProject, - quotaGroup, - quotaNotification, - quotaEnvironment, - quotaRoute, - }) - .toString(), + }) => knex('organization') + .insert({ + id, + name, + friendlyName, + description, + quotaProject, + quotaGroup, + quotaNotification, + quotaEnvironment, + quotaRoute, + }) + .toString(), updateProjectOrganization: ({ pid, patch, }: { pid: number; patch: { [key: string]: any }; - }) => - knex('project') - .where('id', '=', pid) - .update(patch) - .toString(), + }) => knex('project') + .where('id', '=', pid) + .update(patch) + .toString(), addDeployTarget: ({ orgid, - dtid + dtid, }: { orgid?: number; dtid?: number; - }) => - knex('organization_deploy_target') - .insert({ - orgid, - dtid, - }) - .toString(), + }) => knex('organization_deploy_target') + .insert({ + orgid, + dtid, + }) + .toString(), updateOrganization: ({ id, - patch + patch, }: { id: number; patch: { [key: string]: any }; - }) => - knex('organization') - .where('id', '=', id) - .update(patch) - .toString(), - selectOrganization: (id: number) => - knex('organization') - .where('id', '=', id) - .toString(), - selectOrganizationByName: (name: string) => - knex('organization') - .where('name', '=', name) - .toString(), - selectOrganizationProjects: (id: number) => - knex('project') - .where('organization', '=', id) - .toString(), - selectOrganizationProjectIds: (id: number) => - knex('project') - .select(knex.raw('group_concat(id) as project_ids')) - .where('organization', '=', id) - .toString(), - selectOrganizationEnvironments: (id: number) => - knex('organization') - .select('e.*') - .join('project AS p', 'p.organization', '=', 'organization.id') - .join('environment AS e', 'e.project', '=', 'p.id') - .where(knex.raw('organization.id = ?', id)) - .andWhere('e.deleted', '0000-00-00 00:00:00') - .toString(), - selectNotificationsByTypeByOrganizationId: (input) =>{ + }) => knex('organization') + .where('id', '=', id) + .update(patch) + .toString(), + selectOrganization: (id: number) => knex('organization') + .where('id', '=', id) + .toString(), + selectOrganizationByName: (name: string) => knex('organization') + .where('name', '=', name) + .toString(), + selectOrganizationProjects: (id: number) => knex('project') + .where('organization', '=', id) + .toString(), + selectOrganizationProjectIds: (id: number) => knex('project') + .select(knex.raw('group_concat(id) as project_ids')) + .where('organization', '=', id) + .toString(), + selectOrganizationEnvironments: (id: number) => knex('organization') + .select('e.*') + .join('project AS p', 'p.organization', '=', 'organization.id') + .join('environment AS e', 'e.project', '=', 'p.id') + .where(knex.raw('organization.id = ?', id)) + .andWhere('e.deleted', '0000-00-00 00:00:00') + .toString(), + selectNotificationsByTypeByOrganizationId: (input) => { const { type, id, @@ -103,43 +94,37 @@ export const Sql = { return knex(`notification_${type}`) .select( '*', - knex.raw(`'${type}' as type`) + knex.raw(`'${type}' as type`), ) .where('organization', '=', id) .toString(); }, - selectOrganizationDeployTargets: (id: number) => - knex('organization_deploy_target') - .where('orgid', '=', id) - .toString(), - selectDeployTargetsByOrganization: (id: number) => - knex('openshift as dt') - .select('dt.*') - .join('organization_deploy_target as odt', 'dt.id', '=', 'odt.dtid') - .where('odt.orgid', '=', id) - .toString(), - selectDeployTargetsByOrganizationAndDeployTarget: (id: number, dtid: number) => - knex('openshift as dt') - .select('dt.*') - .join('organization_deploy_target as odt', 'dt.id', '=', 'odt.dtid') - .where('odt.orgid', '=', id) - .andWhere('odt.dtid', '=', dtid) - .toString(), - deleteOrganization: (id: number) => - knex('organization') - .where('id', '=', id) - .delete() - .toString(), - deleteOrganizationDeployTargets: (id: number) => - knex('organization_deploy_target') - .where('orgid', '=', id) - .delete() - .toString(), - removeDeployTarget: (orgid: number, dtid: number) => - knex('organization_deploy_target') - .where('orgid', '=', orgid) - .andWhere('dtid', '=', dtid) - .delete() - .toString(), + selectOrganizationDeployTargets: (id: number) => knex('organization_deploy_target') + .where('orgid', '=', id) + .toString(), + selectDeployTargetsByOrganization: (id: number) => knex('openshift as dt') + .select('dt.*') + .join('organization_deploy_target as odt', 'dt.id', '=', 'odt.dtid') + .where('odt.orgid', '=', id) + .toString(), + selectDeployTargetsByOrganizationAndDeployTarget: (id: number, dtid: number) => knex('openshift as dt') + .select('dt.*') + .join('organization_deploy_target as odt', 'dt.id', '=', 'odt.dtid') + .where('odt.orgid', '=', id) + .andWhere('odt.dtid', '=', dtid) + .toString(), + deleteOrganization: (id: number) => knex('organization') + .where('id', '=', id) + .delete() + .toString(), + deleteOrganizationDeployTargets: (id: number) => knex('organization_deploy_target') + .where('orgid', '=', id) + .delete() + .toString(), + removeDeployTarget: (orgid: number, dtid: number) => knex('organization_deploy_target') + .where('orgid', '=', orgid) + .andWhere('dtid', '=', dtid) + .delete() + .toString(), }; diff --git a/services/api/src/resources/problem/helpers.ts b/services/api/src/resources/problem/helpers.ts index 2f96dd37cf..95f2d150d4 100644 --- a/services/api/src/resources/problem/helpers.ts +++ b/services/api/src/resources/problem/helpers.ts @@ -5,12 +5,11 @@ import { Helpers as projectHelpers } from '../project/helpers'; import { Sql } from './sql'; export const Helpers = (sqlClientPool: Pool) => { - const groupByProblemIdentifier = problems => - problems.reduce((obj, problem) => { - obj[problem.identifier] = obj[problem.identifier] || []; - obj[problem.identifier].push(problem); - return obj; - }, {}); + const groupByProblemIdentifier = problems => problems.reduce((obj, problem) => { + obj[problem.identifier] = obj[problem.identifier] || []; + obj[problem.identifier].push(problem); + return obj; + }, {}); const getAllProblems = async (source, environment, envType, severity) => { const environmentType = envType && envType.map(t => t.toLowerCase() || []); @@ -21,24 +20,23 @@ export const Helpers = (sqlClientPool: Pool) => { source, environmentId: environment, environmentType, - severity - }) + severity, + }), ); }; - const getSeverityOptions = async () => - R.map( - R.prop('severity'), - await query(sqlClientPool, Sql.selectSeverityOptions()) - ); + const getSeverityOptions = async () => R.map( + R.prop('severity'), + await query(sqlClientPool, Sql.selectSeverityOptions()), + ); const getProblemsWithProjects = async ( problems, hasPermission, - args: any = [] + args: any = [], ) => { const withProjects = await Object.keys(problems).map(key => { - let projects = problems[key].map(async problem => { + const projects = problems[key].map(async problem => { const envType = !R.isEmpty(args.envType) && args.envType; const { id, @@ -46,15 +44,14 @@ export const Helpers = (sqlClientPool: Pool) => { openshiftProjectName, name, envName, - environmentType - }: any = - (await projectHelpers(sqlClientPool).getProjectByEnvironmentId( - problem.environment, - envType - )) || {}; + environmentType, + }: any = (await projectHelpers(sqlClientPool).getProjectByEnvironmentId( + problem.environment, + envType, + )) || {}; hasPermission('project', 'view', { - project: !R.isNil(project) && project + project: !R.isNil(project) && project, }); return ( @@ -64,7 +61,7 @@ export const Helpers = (sqlClientPool: Pool) => { openshiftProjectName, name, environments: { name: envName }, - type: environmentType + type: environmentType, } ); }); @@ -72,8 +69,8 @@ export const Helpers = (sqlClientPool: Pool) => { return { identifier: key, problem: { ...problem }, - projects: projects, - problems: problems[key] + projects, + problems: problems[key], }; }); @@ -84,6 +81,6 @@ export const Helpers = (sqlClientPool: Pool) => { getAllProblems, getSeverityOptions, groupByProblemIdentifier, - getProblemsWithProjects + getProblemsWithProjects, }; }; diff --git a/services/api/src/resources/problem/resolvers.ts b/services/api/src/resources/problem/resolvers.ts index 3a46f86d08..a2bd072a7d 100644 --- a/services/api/src/resources/problem/resolvers.ts +++ b/services/api/src/resources/problem/resolvers.ts @@ -4,15 +4,15 @@ import { Sql } from './sql'; import { Helpers as problemHelpers } from './helpers'; import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { logger } from '../../loggers/logger'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; export const getAllProblems: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { let rows = []; @@ -22,7 +22,7 @@ export const getAllProblems: ResolverFn = async ( args.source, args.environment, args.envType, - args.severity + args.severity, ); } else { rows = await query( @@ -31,8 +31,8 @@ export const getAllProblems: ResolverFn = async ( source: [], environmentId: 0, environmentType: [], - severity: [] - }) + severity: [], + }), ); } } catch (err) { @@ -42,9 +42,8 @@ export const getAllProblems: ResolverFn = async ( } } - const problems: any = - rows && - rows.map(async problem => { + const problems: any = rows + && rows.map(async problem => { const { environment: envId, name, @@ -55,7 +54,7 @@ export const getAllProblems: ResolverFn = async ( } = problem; await hasPermission('problem', 'view', { - project: project + project, }); return { @@ -65,52 +64,48 @@ export const getAllProblems: ResolverFn = async ( name, project, environmentType, - openshiftProjectName - } + openshiftProjectName, + }, }; }); return Promise.all(problems).then(completed => { const sorted = R.sort(R.descend(R.prop('severity')), completed); - return sorted.map((row: any) => ({ ...(row as Object) })); + return sorted.map((row: any) => ({ ...(row as object) })); }); }; export const getSeverityOptions: ResolverFn = async ( root, args, - { sqlClientPool } -) => { - return await problemHelpers(sqlClientPool).getSeverityOptions(); -}; + { sqlClientPool }, +) => await problemHelpers(sqlClientPool).getSeverityOptions(); export const getProblemSources: ResolverFn = async ( root, args, - { sqlClientPool } -) => { - return R.map( - R.prop('source'), - await query( - sqlClientPool, - 'SELECT DISTINCT source FROM environment_problem' - ) - ); -}; + { sqlClientPool }, +) => R.map( + R.prop('source'), + await query( + sqlClientPool, + 'SELECT DISTINCT source FROM environment_problem', + ), +); export const getProblemsByEnvironmentId: ResolverFn = async ( { id: environmentId }, { severity, source }, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('problem', 'view', { - project: environment.project + project: environment.project, }); } @@ -119,21 +114,21 @@ export const getProblemsByEnvironmentId: ResolverFn = async ( Sql.selectProblemsByEnvironmentId({ environmentId, severity, - source - }) + source, + }), ); - //With some changes in Mariadb, we now have to stringify outgoing json + // With some changes in Mariadb, we now have to stringify outgoing json interface hasData { data: string } rows = R.map((e:hasData) => { e.data = JSON.stringify(e.data); - return e + return e; }, rows); - return R.sort(R.descend(R.prop('created')), rows); + return R.sort(R.descend((r: any) => r.created), rows); }; export const addProblem: ResolverFn = async ( @@ -152,22 +147,22 @@ export const addProblem: ResolverFn = async ( description, version, fixedVersion, - links - } + links, + }, }, { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); await hasPermission('problem', 'add', { - project: environment.project + project: environment.project, }); let insertId: number; try { - ({insertId} = await query( + ({ insertId } = await query( sqlClientPool, Sql.insertProblem({ severity, @@ -180,27 +175,27 @@ export const addProblem: ResolverFn = async ( description, version: version || '', fixed_version: fixedVersion, - links: links, + links, data, - created - }) + created, + }), )); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding problem. Problem already exists.` + 'Error adding problem. Problem already exists.', ); } else { throw new Error(error.message); } - }; + } const rows = await query( sqlClientPool, - Sql.selectProblemByDatabaseId(insertId) + Sql.selectProblemByDatabaseId(insertId), ); - let project = await projectHelpers(sqlClientPool).getProjectById(environment.project); + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); const auditLog: AuditLog = { resource: { @@ -232,15 +227,15 @@ export const addProblem: ResolverFn = async ( description, version: version || '', fixed_version: fixedVersion, - links: links, + links, data: JSON.stringify(data), created, }, ...auditLog, - } + }, }); - let ret = R.prop(0, rows); + const ret = R.prop(0, rows); ret.data = JSON.stringify(data); return ret; @@ -249,17 +244,17 @@ export const addProblem: ResolverFn = async ( export const deleteProblem: ResolverFn = async ( root, { input: { environment: environmentId, identifier, service } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); await hasPermission('problem', 'delete', { - project: environment.project + project: environment.project, }); - let project = await projectHelpers(sqlClientPool).getProjectById(environment.project); + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); await query(sqlClientPool, Sql.deleteProblem(environmentId, identifier, service)); @@ -284,7 +279,7 @@ export const deleteProblem: ResolverFn = async ( payload: { input: { environment, identifier }, ...auditLog, - } + }, }); return 'success'; @@ -293,21 +288,21 @@ export const deleteProblem: ResolverFn = async ( export const deleteProblemsFromSource: ResolverFn = async ( root, { input: { environment: environmentId, source, service } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environmentId); await hasPermission('problem', 'delete', { - project: environment.project + project: environment.project, }); - let project = await projectHelpers(sqlClientPool).getProjectById(environment.project); + const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); await query( sqlClientPool, - Sql.deleteProblemsFromSource(environmentId, source, service) + Sql.deleteProblemsFromSource(environmentId, source, service), ); const auditLog: AuditLog = { @@ -331,7 +326,7 @@ export const deleteProblemsFromSource: ResolverFn = async ( payload: { input: { environment, source, service }, ...auditLog, - } + }, }); return 'success'; diff --git a/services/api/src/resources/problem/sql.ts b/services/api/src/resources/problem/sql.ts index 93972dafe7..f376073671 100644 --- a/services/api/src/resources/problem/sql.ts +++ b/services/api/src/resources/problem/sql.ts @@ -15,7 +15,7 @@ const standardEnvironmentReturn = { links: 'links', data: 'data', created: 'created', - deleted: 'deleted' + deleted: 'deleted', }; export const Sql = { @@ -23,16 +23,16 @@ export const Sql = { source = [], environmentId, environmentType = [], - severity = [] + severity = [], }: { source: string[]; environmentId: number; environmentType: string[]; severity: string[]; }) => { - let q = knex('environment_problem as p') + const q = knex('environment_problem as p') .join('environment as e', { environment: 'e.id' }, '=', { - environment: 'p.environment' + environment: 'p.environment', }) .where('p.deleted', '=', '0000-00-00 00:00:00') .select( @@ -43,8 +43,8 @@ export const Sql = { name: 'e.name', project: 'e.project', environmentType: 'e.environment_type', - openshiftProjectName: 'e.openshift_project_name' - } + openshiftProjectName: 'e.openshift_project_name', + }, ); if (environmentType.length > 0) { @@ -61,20 +61,18 @@ export const Sql = { } return q.toString(); }, - selectSeverityOptions: () => - knex('environment_problem') - .select('severity') - .toString(), - selectProblemByDatabaseId: id => - knex('environment_problem') - .where('id', id) - .toString(), + selectSeverityOptions: () => knex('environment_problem') + .select('severity') + .toString(), + selectProblemByDatabaseId: id => knex('environment_problem') + .where('id', id) + .toString(), selectProblemsByEnvironmentId: ({ environmentId, severity = [], - source = [] + source = [], }) => { - let q = knex('environment_problem') + const q = knex('environment_problem') .select(standardEnvironmentReturn) .where('environment', environmentId) .where('deleted', '=', '0000-00-00 00:00:00'); @@ -99,50 +97,48 @@ export const Sql = { fixed_version, links, data, - created - }) => - knex('environment_problem') - .insert({ - environment, - severity, - severity_score, - identifier, - lagoon_service, - source, - associated_package, - description, - version, - fixed_version, - links, - data, - created - }) - .toString(), + created, + }) => knex('environment_problem') + .insert({ + environment, + severity, + severity_score, + identifier, + lagoon_service, + source, + associated_package, + description, + version, + fixed_version, + links, + data, + created, + }) + .toString(), deleteProblem: (environment, identifier, service) => { - let q = knex('environment_problem') + const q = knex('environment_problem') .where({ - environment: environment, - identifier: identifier + environment, + identifier, }); - if (service != undefined) { + if (service !== undefined) { q.where('lagoon_service', service); } return q.del().toString(); }, deleteProblemsForEnvironment: (environment) => { // This should be used primarily to remove problems when deleting an environment - let q = knex('environment_problem') + const q = knex('environment_problem') .where({ - environment: environment + environment, }); return q.del().toString(); }, - deleteProblemsFromSource: (environment, source, service) => - knex('environment_problem') - .where({ - environment: environment, - source: source, - lagoon_service: service - }) - .del() - .toString() + deleteProblemsFromSource: (environment, source, service) => knex('environment_problem') + .where({ + environment, + source, + lagoon_service: service, + }) + .del() + .toString(), }; diff --git a/services/api/src/resources/problem/types.js b/services/api/src/resources/problem/types.js index 569716b830..e6968bdc49 100644 --- a/services/api/src/resources/problem/types.js +++ b/services/api/src/resources/problem/types.js @@ -1,5 +1,4 @@ import { GraphQLScalarType, GraphQLError } from 'graphql'; -import { makeExecutableSchema } from '@graphql-tools/schema'; import { Kind } from 'graphql/language'; const SeverityScoreType = new GraphQLScalarType({ @@ -9,22 +8,22 @@ const SeverityScoreType = new GraphQLScalarType({ parseValue: parseFloat, parseLiteral(ast) { switch (ast.kind) { - case(Kind.NULL): return null; break; - case(Kind.INT): if(ast.value == 0 || ast.value == 1) { - return ast.value; + case (Kind.NULL): return null; + case (Kind.INT): if (ast.value === 0 || ast.value === 1) { + return ast.value; } - break; - case(Kind.FLOAT): - if(ast.value >= 0 && ast.value <= 1) { - return ast.value; + break; + case (Kind.FLOAT): + if (ast.value >= 0 && ast.value <= 1) { + return ast.value; } } throw new GraphQLError('Severity Score is invalid - should be a one place decimal between 0 and 1 or null'); - } + }, }); const types = { SeverityScoreType, }; -module.exports = types; \ No newline at end of file +module.exports = types; diff --git a/services/api/src/resources/project/helpers.ts b/services/api/src/resources/project/helpers.ts index d56e11aeca..ffc430e019 100644 --- a/services/api/src/resources/project/helpers.ts +++ b/services/api/src/resources/project/helpers.ts @@ -1,6 +1,6 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; -import { asyncPipe } from '@lagoon/commons/dist/util/func'; +import { asyncPipe } from '../../commons/util/func'; import { query } from '../../util/db'; import { Sql } from './sql'; import { Sql as environmentSql } from '../environment/sql'; @@ -21,21 +21,20 @@ export const Helpers = (sqlClientPool: Pool) => { try { // finally check the user view:project permission await hasPermission('project', 'view', { - project: project.id + project: project.id, }); - return } catch (err) { if (project.organization != null) { await hasPermission('organization', 'viewProject', { - organization: project.organization + organization: project.organization, }); // if the organization owner has permission to view project, return - return + return; } - throw err + throw err; } } - } + }; const checkOrgProjectUpdatePermission = async (hasPermission, pid) => { // helper checks the permission to updateProject:organization // or the update:project permission @@ -44,30 +43,25 @@ export const Helpers = (sqlClientPool: Pool) => { try { // finally check the user update:project permission await hasPermission('project', 'update', { - project: project.id + project: project.id, }); - return } catch (err) { if (project.organization != null) { await hasPermission('organization', 'updateProject', { - organization: project.organization + organization: project.organization, }); // if the organization owner has permission to update project, return - return + return; } - throw err + throw err; } - } - - const aliasOpenshiftToK8s = (projects: any[]) => { - return projects.map(project => { - return { - ...project, - kubernetes: project.openshift, - }; - }); }; + const aliasOpenshiftToK8s = (projects: any[]) => projects.map(project => ({ + ...project, + kubernetes: project.openshift, + })); + const getProjectById = async (id: number) => { const rows = await query(sqlClientPool, Sql.selectProject(id)); const withK8s = aliasOpenshiftToK8s(rows); @@ -82,7 +76,7 @@ export const Helpers = (sqlClientPool: Pool) => { const getProjectByEnvironmentId = async ( environmentId: number, - environmentType = [] + environmentType = [], ): Promise<{ id: number // environment id envName: string @@ -95,23 +89,22 @@ export const Helpers = (sqlClientPool: Pool) => { }> => { const rows = await query( sqlClientPool, - Sql.selectProjectByEnvironmentId(environmentId, environmentType) + Sql.selectProjectByEnvironmentId(environmentId, environmentType), ); return R.prop(0, rows); }; const getProjectByOrganizationId = async ( - organizationId: number + organizationId: number, ) => { const rows = await query( sqlClientPool, - Sql.selectProjectsByOrganizationId(organizationId) + Sql.selectProjectsByOrganizationId(organizationId), ); return rows; }; - const getProjectsByIds = (projectIds: number[]) => - query(sqlClientPool, Sql.selectProjectsByIds(projectIds)); + const getProjectsByIds = (projectIds: number[]) => query(sqlClientPool, Sql.selectProjectsByIds(projectIds)); const checkApiRoutesFeature = async (organizationId: number) => { const organization = await organizationHelpers(sqlClientPool).getOrganizationById(organizationId); @@ -135,13 +128,13 @@ export const Helpers = (sqlClientPool: Pool) => { getProjectIdByName: async (name: string): Promise => { const pidResult = await query( sqlClientPool, - Sql.selectProjectIdByName(name) + Sql.selectProjectIdByName(name), ); const amount = R.length(pidResult); if (amount > 1) { throw new Error( - `Multiple project candidates for '${name}' (${amount} found). Do nothing.` + `Multiple project candidates for '${name}' (${amount} found). Do nothing.`, ); } @@ -184,37 +177,34 @@ export const Helpers = (sqlClientPool: Pool) => { R.T, () => { throw new Error('Must provide project "id" or "name"'); - } - ] + }, + ], ])(projectInput); }, getAllProjects: async () => query(sqlClientPool, Sql.selectAllProjects()), - getAllProjectsNotIn: async ids => - query(sqlClientPool, Sql.selectAllProjectNotIn(ids)), - getAllProjectsIn: async ids => - query(sqlClientPool, Sql.selectAllProjectsIn(ids)), - getAllProjectNames: async () => - R.map( - R.prop('name'), - await query(sqlClientPool, Sql.selectAllProjectNames()) - ), + getAllProjectsNotIn: async ids => query(sqlClientPool, Sql.selectAllProjectNotIn(ids)), + getAllProjectsIn: async ids => query(sqlClientPool, Sql.selectAllProjectsIn(ids)), + getAllProjectNames: async () => R.map( + R.prop('name'), + await query(sqlClientPool, Sql.selectAllProjectNames()), + ), deleteProjectById: async (id: number) => { // logger.debug(`deleting project ${id} notifications`) await query( sqlClientPool, - Sql.deleteNotifications(id) + Sql.deleteNotifications(id), ); // logger.debug(`deleting project ${id} environment variables`) // clean up environment variables for project await query( sqlClientPool, - Sql.deleteEnvironmentVariables(id) + Sql.deleteEnvironmentVariables(id), ); // logger.debug(`deleting project ${id} deploytarget configurations`) // clean up deploytarget configurations await query( sqlClientPool, - Sql.deleteDeployTargetConfigs(id) + Sql.deleteDeployTargetConfigs(id), ); // logger.debug(`deleting project ${id} environment leftover backups rows`) // clean up backups table so backups for environments don't remain in limbo once the project is deleted @@ -225,19 +215,19 @@ export const Helpers = (sqlClientPool: Pool) => { const projectEnvironmentIds = await query(sqlClientPool, environmentSql.selectEnvironmentsByProjectID(id, true)); await query( sqlClientPool, - backupSql.deleteBackupsByEnvironmentIds(projectEnvironmentIds) + backupSql.deleteBackupsByEnvironmentIds(projectEnvironmentIds), ); // same for environment storage // logger.debug(`deleting project ${id} environment leftover storage rows`) await query( sqlClientPool, - environmentSql.deleteEnvironmentStorageByEnvironmentIds(projectEnvironmentIds) + environmentSql.deleteEnvironmentStorageByEnvironmentIds(projectEnvironmentIds), ); // logger.debug(`deleting project ${id} leftover environment rows`) // clean up environments table so environments don't remain in limbo once the project is deleted await query( sqlClientPool, - environmentSql.deleteEnvironmentsByProjectID(id) + environmentSql.deleteEnvironmentsByProjectID(id), ); // delete any autogenerated route configuration for this project await routeHelpers(sqlClientPool).deleteAutogeneratedRouteConfigForProject(id); @@ -245,8 +235,8 @@ export const Helpers = (sqlClientPool: Pool) => { // delete the project await query( sqlClientPool, - Sql.deleteProject(id) + Sql.deleteProject(id), ); - } + }, }; }; diff --git a/services/api/src/resources/project/resolvers.ts b/services/api/src/resources/project/resolvers.ts index bd1d57f717..5430077eee 100644 --- a/services/api/src/resources/project/resolvers.ts +++ b/services/api/src/resources/project/resolvers.ts @@ -1,45 +1,44 @@ import * as R from 'ramda'; import validator from 'validator'; -import { ResolverFn } from '../'; +import GitUrlParse from 'git-url-parse'; +import { ResolverFn } from '..'; import { logger } from '../../loggers/logger'; import { knex, query, isPatchEmpty } from '../../util/db'; import { validateKey, generatePrivateKey as genpk } from '../../util/func'; import { Helpers } from './helpers'; import { Sql } from './sql'; -import { Sql as SshKeySql} from '../sshKey/sql'; +import { Sql as SshKeySql, Sql as sshKeySql } from '../sshKey/sql'; import * as OS from '../openshift/sql'; -import { Sql as sshKeySql } from '../sshKey/sql'; import { Helpers as organizationHelpers } from '../organization/helpers'; import { Helpers as notificationHelpers } from '../notification/helpers'; import { Helpers as groupHelpers } from '../group/helpers'; import { getUserProjectIdsFromRoleProjectIds } from '../../util/auth'; -import { AuditType } from '@lagoon/commons/dist/types'; -import GitUrlParse from 'git-url-parse'; -import { AuditLog, AuditResource } from '../audit/types'; +import { AuditType } from '../../commons/types'; +import { AuditLog } from '../audit/types'; -const DISABLE_NON_ORGANIZATION_PROJECT_CREATION = process.env.DISABLE_NON_ORGANIZATION_PROJECT_CREATION || "false" +const DISABLE_NON_ORGANIZATION_PROJECT_CREATION = process.env.DISABLE_NON_ORGANIZATION_PROJECT_CREATION || 'false'; const isValidGitUrl = value => { try { - GitUrlParse(value) - return true - } catch (error) { - return false + GitUrlParse(value); + return true; + } catch (_error) { + return false; } -} +}; export const getPrivateKey: ResolverFn = async ( project, _args, - { hasPermission } + { hasPermission }, ) => { try { await hasPermission('project', 'viewPrivateKey', { - project: project.id + project: project.id, }); return project.privateKey; - } catch (err) { + } catch (_err) { return null; } }; @@ -47,28 +46,32 @@ export const getPrivateKey: ResolverFn = async ( export const getProjectDeployKey: ResolverFn = async ( project, _args, - _ + _, ) => { try { - const publickey = await validateKey(R.prop('privateKey', project), "private") + const publickey = await validateKey(R.prop('privateKey', project), 'private'); - return publickey['publickey'] - } catch (err) { + return publickey.publickey; + } catch (_err) { return null; } }; export const getAllProjects: ResolverFn = async ( root, - { order, createdAfter, gitUrl, buildImage }, - { sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups } + { + order, createdAfter, gitUrl, buildImage, + }, + { + sqlClientPool, hasPermission, models, keycloakGrant, keycloakUsersGroups, + }, ) => { let userProjectIds: number[]; try { // admin check, if passed then pre-set authz await hasPermission('project', 'viewAll'); - } catch (err) { + } catch (_err) { // else user if (!keycloakGrant) { logger.debug('No grant available for getAllProjects'); @@ -110,7 +113,7 @@ export const getAllProjects: ResolverFn = async ( export const getProjectByEnvironmentId: ResolverFn = async ( { id: eid }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const rows = await query(sqlClientPool, Sql.selectProjectByEnvironmentID(eid)); @@ -118,14 +121,14 @@ export const getProjectByEnvironmentId: ResolverFn = async ( const project = withK8s[0]; - await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes) + await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes); return project; }; export const getProjectById: ResolverFn = async ( { project: pid }, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const rows = await query(sqlClientPool, Sql.selectProjectById(pid)); @@ -133,7 +136,7 @@ export const getProjectById: ResolverFn = async ( const project = withK8s[0]; - await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes) + await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes); return project; }; @@ -141,7 +144,7 @@ export const getProjectById: ResolverFn = async ( export const getProjectByGitUrl: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const rows = await query(sqlClientPool, Sql.selectProjectByGitUrl(args.gitUrl)); @@ -149,7 +152,7 @@ export const getProjectByGitUrl: ResolverFn = async ( const project = withK8s[0]; - await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes) + await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes); return project; }; @@ -157,7 +160,7 @@ export const getProjectByGitUrl: ResolverFn = async ( export const getProjectByName: ResolverFn = async ( root, args, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const rows = await query(sqlClientPool, Sql.selectProjectByName(args.name)); @@ -168,7 +171,7 @@ export const getProjectByName: ResolverFn = async ( return null; } - await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes) + await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id, adminScopes); return project; }; @@ -176,15 +179,17 @@ export const getProjectByName: ResolverFn = async ( export const getProjectsByMetadata: ResolverFn = async ( root, { metadata }, - { sqlClientPool, hasPermission, keycloakGrant, models, keycloakUsersGroups }, - info + { + sqlClientPool, hasPermission, keycloakGrant, models, keycloakUsersGroups, + }, + _info, ) => { let userProjectIds: number[]; try { // admin check, if passed then pre-set authz await hasPermission('project', 'viewAll'); - } catch (err) { + } catch (_err) { if (!keycloakGrant) { logger.debug('No grant available for getProjectsByMetadata'); return []; @@ -205,11 +210,10 @@ export const getProjectsByMetadata: ResolverFn = async ( if (meta_value) { queryBuilder = queryBuilder.whereRaw('JSON_CONTAINS(metadata, ?, ?)'); queryArgs = [...queryArgs, `"${meta_value}"`, `$.${meta_key}`]; - } - // Support key-only queries. - else { + } else { + // Support key-only queries. queryBuilder = queryBuilder.whereRaw( - "JSON_CONTAINS_PATH(metadata, 'one', ?)" + "JSON_CONTAINS_PATH(metadata, 'one', ?)", ); queryArgs = [...queryArgs, `$.${meta_key}`]; } @@ -224,67 +228,65 @@ export const getProjectsByMetadata: ResolverFn = async ( export const addProject = async ( root, { input }, - { hasPermission, sqlClientPool, models, keycloakGrant, userActivityLogger, adminScopes } + { + hasPermission, sqlClientPool, models, keycloakGrant, userActivityLogger, adminScopes, + }, ) => { - // Add the user who submitted this request to the project let userAlreadyHasAccess = false; if (adminScopes.platformOwner) { - userAlreadyHasAccess = true + userAlreadyHasAccess = true; } if (input.organization != null) { await hasPermission('organization', 'addProject', { - organization: input.organization + organization: input.organization, }); // check the project quota before adding the project const organization = await organizationHelpers(sqlClientPool).getOrganizationById(input.organization); if (!organization) { // org doesn't exist, unauth throw new Error( - `Unauthorized: You don't have permission to "addProject" on "organization"` + 'Unauthorized: You don\'t have permission to "addProject" on "organization"', ); } // if the project is created without the addOrgOwner boolean set to true, then do not add the user to the project as its owner if (!input.addOrgOwner) { - userAlreadyHasAccess = true + userAlreadyHasAccess = true; } const projects = await organizationHelpers(sqlClientPool).getProjectsByOrganizationId(input.organization); - if (projects.length >= organization.quotaProject && organization.quotaProject != -1) { + if (projects.length >= organization.quotaProject && organization.quotaProject !== -1) { throw new Error( - `This would exceed this organizations project quota; ${projects.length}/${organization.quotaProject}` + `This would exceed this organizations project quota; ${projects.length}/${organization.quotaProject}`, ); } const deploytarget = input.kubernetes || input.openshift; if (deploytarget) { const deploytargets = await organizationHelpers(sqlClientPool).getDeployTargetsByOrganizationId(input.organization); - let validDeployTarget = false + let validDeployTarget = false; for (const dt of deploytargets) { - if (dt.dtid == deploytarget) { - validDeployTarget = true + if (dt.dtid === deploytarget) { + validDeployTarget = true; } } if (!validDeployTarget) { throw new Error('The provided deploytarget is not valid for this organization'); } } - + } else if (DISABLE_NON_ORGANIZATION_PROJECT_CREATION === 'false' || adminScopes.platformOwner) { + await hasPermission('project', 'add'); } else { - if (DISABLE_NON_ORGANIZATION_PROJECT_CREATION == "false" || adminScopes.platformOwner) { - await hasPermission('project', 'add'); - } else { - throw new Error( - 'Project creation is restricted to organizations only' - ); - } + throw new Error( + 'Project creation is restricted to organizations only', + ); } - if (input.name.trim().length == 0) { + if (input.name.trim().length === 0) { throw new Error( - 'A project name must be provided!' + 'A project name must be provided!', ); } if (validator.matches(input.name, /[^0-9a-z-]/)) { throw new Error( - 'Only lowercase characters, numbers and dashes allowed for name!' + 'Only lowercase characters, numbers and dashes allowed for name!', ); } if (validator.matches(input.name, /--/)) { @@ -301,36 +303,36 @@ export const addProject = async ( // check if project already exists before doing anything else const pidResult = await query( sqlClientPool, - Sql.selectProjectIdByName(input.name) + Sql.selectProjectIdByName(input.name), ); if (R.length(pidResult) >= 1) { throw new Error( - `Error creating project '${input.name}'. Project already exists.` + `Error creating project '${input.name}'. Project already exists.`, ); } let keyPair: any = {}; try { if (R.prop('privateKey', input)) { - const publickey = await validateKey(R.prop('privateKey', input), "private") - if (!publickey['sha256fingerprint']) { + const publickey = await validateKey(R.prop('privateKey', input), 'private'); + if (!publickey.sha256fingerprint) { throw new Error('private key failed validation'); } keyPair = { ...keyPair, private: R.replace(/\n/g, '\n', (R.prop('privateKey', input)).toString('openssh')), - public: publickey['publickey'], - fingerprint: publickey['sha256fingerprint'], - type: publickey['type'] + public: publickey.publickey, + fingerprint: publickey.sha256fingerprint, + type: publickey.type, }; } else { - const genkey = await genpk() + const genkey = await genpk(); keyPair = { ...keyPair, - private: genkey['privatekeypem'], - public: genkey['publickey'], - fingerprint: genkey['sha256fingerprint'], - type: genkey['type'] + private: genkey.privatekeypem, + public: genkey.publickey, + fingerprint: genkey.sha256fingerprint, + type: genkey.type, }; } } catch (err) { @@ -339,47 +341,37 @@ export const addProject = async ( if (input.deploymentsDisabled && !adminScopes.platformOwner) { // only platform owner can set this, it won't return an error, just ignores the value - delete input.deploymentsDisabled + delete input.deploymentsDisabled; } if (input.storageCalc && !adminScopes.platformOwner) { // only platform owner can set this, it won't return an error, just ignores the value - delete input.storageCalc + delete input.storageCalc; } - let buildImage = null; - if (input.buildImage) { - if (adminScopes.platformOwner) { - buildImage = input.buildImage - } else { - throw new Error('Setting build image is only available to administrators.'); - } + if (input.buildImage && !adminScopes.platformOwner) { + throw new Error('Setting build image is only available to administrators.'); } - let sharedBaasBucket = null; - if(typeof input.sharedBaasBucket == "boolean") { - if (adminScopes.platformOwner) { - sharedBaasBucket = input.sharedBaasBucket - } else { - throw new Error('Setting shared baas bucket is only available to administrators.'); - } + if (typeof input.sharedBaasBucket === 'boolean' && !adminScopes.platformOwner) { + throw new Error('Setting shared baas bucket is only available to administrators.'); } const osRows = await query(sqlClientPool, OS.Sql.selectOpenshift(openshift)); - if(osRows.length == 0) { + if (osRows.length === 0) { throw Error(`Openshift ID: "${openshift}" does not exist"`); } const { insertId } = await query( sqlClientPool, Sql.createProject({ - ...input, - openshift, - privateKey: keyPair.private - })); + ...input, + openshift, + privateKey: keyPair.private, + })); const rows = await query( - sqlClientPool, Sql.selectProject(insertId) + sqlClientPool, Sql.selectProject(insertId), ); const withK8s = Helpers(sqlClientPool).aliasOpenshiftToK8s(rows); @@ -387,11 +379,11 @@ export const addProject = async ( // Create a default group for this project let projectDefaultGroup; - let attributes = { + const attributes = { type: ['project-default-group'], // lagoon-projects and group-lagoon-project-ids attributes are added for legacy reasons only, theses values are stored in the api-db now 'lagoon-projects': [project.id], - 'group-lagoon-project-ids': [`{${JSON.stringify(`project-${project.name}`)}:[${project.id}]}`] + 'group-lagoon-project-ids': [`{${JSON.stringify(`project-${project.name}`)}:[${project.id}]}`], }; // add the organization attribute if this exists if (input.organization != null) { @@ -401,11 +393,11 @@ export const addProject = async ( try { projectDefaultGroup = await models.GroupModel.addGroup({ name: `project-${project.name}`, - attributes: attributes + attributes, }, project.id, input.organization); } catch (err) { logger.error( - `Could not create default project group for ${project.name}: ${err.message}` + `Could not create default project group for ${project.name}: ${err.message}`, ); } @@ -415,17 +407,17 @@ export const addProject = async ( defaultUser = await models.UserModel.addUser({ email: `default-user@${project.name}`, username: `default-user@${project.name}`, - comment: `autogenerated user for project ${project.name}` + comment: `autogenerated user for project ${project.name}`, }); await models.GroupModel.addUserToGroup(defaultUser, projectDefaultGroup, 'maintainer'); } catch (err) { - logger.error(`Could not create default user for project ${project.name}: ${err.message}`) + logger.error(`Could not create default user for project ${project.name}: ${err.message}`); } // Check if an existing user has the public key linked to them const userRows = await query( sqlClientPool, - sshKeySql.selectUserIdsBySshKeyFingerprint(keyPair.fingerprint) + sshKeySql.selectUserIdsBySshKeyFingerprint(keyPair.fingerprint), ); const userId = R.path([0, 'usid'], userRows); if (!userId) { @@ -439,16 +431,16 @@ export const addProject = async ( name: `default-user@${project.name}`, keyValue: keyParts[1], keyType: keyParts[0], - keyFingerprint: keyPair.fingerprint - }) + keyFingerprint: keyPair.fingerprint, + }), ); await query( sqlClientPool, - sshKeySql.addSshKeyToUser({ sshKeyId: insertId, userId: defaultUser.id }) + sshKeySql.addSshKeyToUser({ sshKeyId: insertId, userId: defaultUser.id }), ); } catch (err) { logger.error( - `Could not create default project user for ${project.name}: ${err.message}` + `Could not create default project user for ${project.name}: ${err.message}`, ); } } else { @@ -458,21 +450,21 @@ export const addProject = async ( await models.GroupModel.addUserToGroup(user, projectDefaultGroup, 'maintainer'); } catch (err) { logger.error( - `Could not link existing user to default project group for ${project.name}: ${err.message}` + `Could not link existing user to default project group for ${project.name}: ${err.message}`, ); } } if (!userAlreadyHasAccess && keycloakGrant) { const user = await models.UserModel.loadUserById( - keycloakGrant.access_token.content.sub + keycloakGrant.access_token.content.sub, ); try { await models.GroupModel.addUserToGroup(user, projectDefaultGroup, 'owner'); } catch (err) { logger.error( - `Could not link requesting user to default project group for ${project.name}: ${err.message}` + `Could not link requesting user to default project group for ${project.name}: ${err.message}`, ); } } @@ -494,7 +486,7 @@ export const addProject = async ( input, data: project, ...auditLog, - } + }, }); return project; @@ -503,7 +495,9 @@ export const addProject = async ( export const deleteProject: ResolverFn = async ( _root, { input: { project: projectName } }, - { sqlClientPool, hasPermission, userActivityLogger, models } + { + sqlClientPool, hasPermission, userActivityLogger, models, + }, ) => { // Will throw on invalid conditions const pid = await Helpers(sqlClientPool).getProjectIdByName(projectName); @@ -513,32 +507,32 @@ export const deleteProject: ResolverFn = async ( // otherwise fall back to the non-organization permission check if (project.organization != null) { await hasPermission('organization', 'deleteProject', { - organization: project.organization + organization: project.organization, }); } else { await hasPermission('project', 'delete', { - project: pid + project: pid, }); } // check for existing environments const rows = await query( - sqlClientPool, Sql.selectEnvironmentsByProjectId(pid) + sqlClientPool, Sql.selectEnvironmentsByProjectId(pid), ); if (rows.length > 0) { // throw error if there are any existing environments throw new Error( - 'Unable to delete project, there are existing environments that need to be removed first' + 'Unable to delete project, there are existing environments that need to be removed first', ); } try { // remove all notifications from project - await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({project: pid}) + await notificationHelpers(sqlClientPool).removeAllNotificationsFromProject({ project: pid }); } catch (err) { logger.error( - `Could not remove notifications from project ${project.name}: ${err.message}` + `Could not remove notifications from project ${project.name}: ${err.message}`, ); } @@ -546,35 +540,35 @@ export const deleteProject: ResolverFn = async ( // Remove the project from all groups it is associated to try { - const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid) + const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, pid); await models.GroupModel.removeProjectFromGroups(pid, projectGroups); } catch (err) { logger.error( - `Could not remove project from associated groups ${project.name}: ${err.message}` + `Could not remove project from associated groups ${project.name}: ${err.message}`, ); } // Remove the default project group try { const group = await models.GroupModel.loadSparseGroupByName( - `project-${project.name}` + `project-${project.name}`, ); await models.GroupModel.deleteGroup(group.id); } catch (err) { logger.error( - `Could not delete default group for project ${project.name}: ${err.message}` + `Could not delete default group for project ${project.name}: ${err.message}`, ); } // Remove the default user try { const user = await models.UserModel.loadUserByEmail( - `default-user@${project.name}` + `default-user@${project.name}`, ); await models.UserModel.deleteUser(user.id); } catch (err) { logger.error( - `Could not delete default user for project ${project.name}: ${err.message}` + `Could not delete default user for project ${project.name}: ${err.message}`, ); } @@ -593,10 +587,10 @@ export const deleteProject: ResolverFn = async ( event: 'api:deleteProject', payload: { input: { - project + project, }, ...auditLog, - } + }, }); return 'success'; @@ -633,14 +627,16 @@ export const updateProject: ResolverFn = async ( developmentEnvironmentsLimit, organization, buildImage, - sharedBaasBucket - } - } + sharedBaasBucket, + }, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, models, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, models, adminScopes } ) => { await hasPermission('project', 'update', { - project: id + project: id, }); // check if a user has permission to disable deployments of a project or not @@ -657,7 +653,7 @@ export const updateProject: ResolverFn = async ( } } - if(typeof sharedBaasBucket == "boolean") { + if (typeof sharedBaasBucket === 'boolean') { if (!adminScopes.platformOwner) { throw new Error('Setting shared baas bucket is only available to administrators.'); } @@ -675,14 +671,14 @@ export const updateProject: ResolverFn = async ( } if (typeof name === 'string') { - if (name.trim().length == 0) { + if (name.trim().length === 0) { throw new Error( - 'A project name must be provided!' + 'A project name must be provided!', ); } if (validator.matches(name, /[^0-9a-z-]/)) { throw new Error( - 'Only lowercase characters, numbers and dashes allowed for name!' + 'Only lowercase characters, numbers and dashes allowed for name!', ); } } @@ -708,17 +704,16 @@ export const updateProject: ResolverFn = async ( if (patch.privateKey && patch.privateKey !== oldProject.privateKey) { let keyPair: any = {}; try { - - const publickey = await validateKey(R.prop('privateKey', patch), "private") - if (!publickey['sha256fingerprint']) { + const publickey = await validateKey(R.prop('privateKey', patch), 'private'); + if (!publickey.sha256fingerprint) { throw new Error('new private key failed validation'); } keyPair = { ...keyPair, private: R.replace(/\n/g, '\n', (R.prop('privateKey', patch)).toString('openssh')), - public: publickey['publickey'], - fingerprint: publickey['sha256fingerprint'], - type: publickey['type'] + public: publickey.publickey, + fingerprint: publickey.sha256fingerprint, + type: publickey.type, }; const keyParts = keyPair.public.split(' '); @@ -730,38 +725,38 @@ export const updateProject: ResolverFn = async ( name: `default-user@${oldProject.name}`, keyValue: keyParts[1], keyType: keyParts[0], - keyFingerprint: keyPair.fingerprint - }) + keyFingerprint: keyPair.fingerprint, + }), ); const user = await models.UserModel.loadUserByEmail( - `default-user@${oldProject.name}` + `default-user@${oldProject.name}`, ); await query( sqlClientPool, - sshKeySql.addSshKeyToUser({ sshKeyId: insertId, userId: user.id }) + sshKeySql.addSshKeyToUser({ sshKeyId: insertId, userId: user.id }), ); // remove the old public key from the default user - const oldKey = await validateKey(R.prop('privateKey', oldProject), "private") - if (!oldKey['sha256fingerprint']) { + const oldKey = await validateKey(R.prop('privateKey', oldProject), 'private'); + if (!oldKey.sha256fingerprint) { throw new Error('old private key failed validation'); } const skidResult = await query( sqlClientPool, - SshKeySql.selectSshKeyByFingerprint(oldKey['sha256fingerprint']) + SshKeySql.selectSshKeyByFingerprint(oldKey.sha256fingerprint), ); const skid = R.path(['0', 'id'], skidResult) as number; await query( sqlClientPool, - SshKeySql.deleteUserSshKeyByKeyId(skid) + SshKeySql.deleteUserSshKeyByKeyId(skid), ); await query( sqlClientPool, - SshKeySql.deleteSshKeyByKeyId(skid) + SshKeySql.deleteSshKeyByKeyId(skid), ); } catch (err) { logger.error( - `Could not update default project user for ${oldProject.name}: ${err.message}` + `Could not update default project user for ${oldProject.name}: ${err.message}`, ); } } catch (err) { @@ -826,40 +821,40 @@ export const updateProject: ResolverFn = async ( developmentEnvironmentsLimit, organization, buildImage, - sharedBaasBucket - } - }) + sharedBaasBucket, + }, + }), ); // Rename the default group and user if (patch.name && oldProject.name !== patch.name) { try { const group = await models.GroupModel.loadSparseGroupByName( - `project-${oldProject.name}` + `project-${oldProject.name}`, ); await models.GroupModel.updateGroup({ id: group.id, - name: `project-${patch.name}` + name: `project-${patch.name}`, }); } catch (err) { logger.error( - `Could not rename default group for project ${patch.name}: ${err.message}` + `Could not rename default group for project ${patch.name}: ${err.message}`, ); } try { const user = await models.UserModel.loadUserByEmail( - `default-user@${oldProject.name}` + `default-user@${oldProject.name}`, ); await models.UserModel.updateUser({ id: user.id, email: `default-user@${patch.name}`, username: `default-user@${patch.name}`, - comment: `autogenerated user for project ${patch.name}` + comment: `autogenerated user for project ${patch.name}`, }); } catch (err) { logger.error( - `Could not rename default user for project ${patch.name}: ${err.message}` + `Could not rename default user for project ${patch.name}: ${err.message}`, ); } } @@ -935,10 +930,10 @@ export const updateProject: ResolverFn = async ( developmentEnvironmentsLimit, organization, buildImage, - sharedBaasBucket + sharedBaasBucket, }, ...auditLog, - } + }, }); return Helpers(sqlClientPool).getProjectById(id); @@ -947,10 +942,10 @@ export const updateProject: ResolverFn = async ( export const removeProjectMetadataByKey: ResolverFn = async ( root, { input: { id, key } }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('project', 'update', { - project: id + project: id, }); if (!key) { @@ -960,7 +955,7 @@ export const removeProjectMetadataByKey: ResolverFn = async ( if (typeof key === 'string') { if (validator.matches(key, /[^0-9a-z-]/)) { throw new Error( - 'Only lowercase characters, numbers and dashes allowed for key!' + 'Only lowercase characters, numbers and dashes allowed for key!', ); } } @@ -972,7 +967,7 @@ export const removeProjectMetadataByKey: ResolverFn = async ( `UPDATE project SET metadata = JSON_REMOVE(metadata, :meta_key) WHERE id = :id`, - { id, meta_key: `$.${key}` } + { id, meta_key: `$.${key}` }, ); const auditLog: AuditLog = { @@ -991,10 +986,10 @@ export const removeProjectMetadataByKey: ResolverFn = async ( payload: { input: { id, - key + key, }, ...auditLog, - } + }, }); return Helpers(sqlClientPool).getProjectById(id); @@ -1006,13 +1001,13 @@ export const updateProjectMetadata: ResolverFn = async ( input: { id, patch, - patch: { key, value } - } + patch: { key, value }, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { await hasPermission('project', 'update', { - project: id + project: id, }); if (isPatchEmpty({ patch })) { @@ -1026,7 +1021,7 @@ export const updateProjectMetadata: ResolverFn = async ( if (typeof key === 'string') { if (validator.matches(key, /[^0-9a-z-]/)) { throw new Error( - 'Only lowercase characters, numbers and dashes allowed for key!' + 'Only lowercase characters, numbers and dashes allowed for key!', ); } } @@ -1041,8 +1036,8 @@ export const updateProjectMetadata: ResolverFn = async ( { id, meta_key: `$.${key}`, - meta_value: value - } + meta_value: value, + }, ); const auditLog: AuditLog = { @@ -1055,23 +1050,22 @@ export const updateProjectMetadata: ResolverFn = async ( if (project.organization) { auditLog.organizationId = project.organization; } - userActivityLogger(`User updated project metadata`, { + userActivityLogger('User updated project metadata', { project: '', event: 'api:updateProjectMetadata', payload: { patch: { project: id, key, - value + value, }, ...auditLog, - } + }, }); return Helpers(sqlClientPool).getProjectById(id); }; - /* getFeatureApiRoutes is a field resolver it has no permission checks as it isn't called directly @@ -1081,7 +1075,5 @@ export const updateProjectMetadata: ResolverFn = async ( export const getFeatureApiRoutes: ResolverFn = async ( input, args, - { sqlClientPool } -) => { - return Helpers(sqlClientPool).checkApiRoutesFeature(input.organization); -}; + { sqlClientPool }, +) => Helpers(sqlClientPool).checkApiRoutesFeature(input.organization); diff --git a/services/api/src/resources/project/sql.ts b/services/api/src/resources/project/sql.ts index 631947dc8d..55b0860e23 100644 --- a/services/api/src/resources/project/sql.ts +++ b/services/api/src/resources/project/sql.ts @@ -1,93 +1,76 @@ import { knex } from '../../util/db'; export const Sql = { - selectProject: (id: number) => - knex('project') - .where('id', id) - .toString(), - selectAllProjectNames: () => - knex('project') - .select('name') - .toString(), + selectProject: (id: number) => knex('project') + .where('id', id) + .toString(), + selectAllProjectNames: () => knex('project') + .select('name') + .toString(), selectAllProjects: () => knex('project').toString(), - selectAllProjectNotIn: (ids: number) => - knex('project') - .select('id', 'name') - .whereNotIn('id', ids) - .orderBy('id', 'asc') - .toString(), - selectAllProjectIDsNotIn: (ids) => - knex('project') - .select(knex.raw('group_concat(id) as project_ids')) - .whereNotIn('id', ids) - .orderBy('id', 'asc') - .toString(), - selectAllProjectsIn: (ids: number) => - knex('project') - .select('id') - .whereIn('id', ids) - .toString(), - selectProjectByName: (name: string) => - knex('project') - .where('name', name) - .toString(), - selectProjectById: (id: number) => - knex('project') - .where('id', id) - .limit(1) - .toString(), - selectProjectIdByName: (name: string) => - knex('project') - .where('name', name) - .select('id') - .toString(), - selectProjectByGitUrl: (git_url: string) => - knex('project') - .where('git_url', git_url) - .limit(1) - .toString(), - selectProjectsByIds: (projectIds: number[]) => - knex('project AS p') - .whereIn('p.id', projectIds) - .toString(), - selectProjectsByOrganizationId: (organizationId: number) => - knex('project as p') - .where('p.organization', organizationId) - .toString(), - selectProjectByEnvironmentID: (id: number) => - knex('environment as e') - .select('project.*') - .join('project', 'e.project', '=', 'project.id') - .where(knex.raw('e.id = ?', id)) - .limit(1) - .toString(), - deleteEnvironmentVariables: (id: number) => - knex('env_vars') - .where('project', '=', id) - .delete() - .toString(), - deleteDeployTargetConfigs: (id: number) => - knex('deploy_target_config') - .where('project', '=', id) - .delete() - .toString(), - deleteNotifications: (id: number) => - knex('project_notification') - .where('pid', '=', id) - .delete() - .toString(), - deleteProject: (id: number) => - knex('project') - .where('id', '=', id) - .delete() - .toString(), - selectEnvironmentsByProjectId: (id: number) => - knex('environment as e') - .where('e.project', '=', id) - .andWhere('e.deleted', '0000-00-00 00:00:00') - .toString(), + selectAllProjectNotIn: (ids: number) => knex('project') + .select('id', 'name') + .whereNotIn('id', ids) + .orderBy('id', 'asc') + .toString(), + selectAllProjectIDsNotIn: (ids) => knex('project') + .select(knex.raw('group_concat(id) as project_ids')) + .whereNotIn('id', ids) + .orderBy('id', 'asc') + .toString(), + selectAllProjectsIn: (ids: number) => knex('project') + .select('id') + .whereIn('id', ids) + .toString(), + selectProjectByName: (name: string) => knex('project') + .where('name', name) + .toString(), + selectProjectById: (id: number) => knex('project') + .where('id', id) + .limit(1) + .toString(), + selectProjectIdByName: (name: string) => knex('project') + .where('name', name) + .select('id') + .toString(), + selectProjectByGitUrl: (git_url: string) => knex('project') + .where('git_url', git_url) + .limit(1) + .toString(), + selectProjectsByIds: (projectIds: number[]) => knex('project AS p') + .whereIn('p.id', projectIds) + .toString(), + selectProjectsByOrganizationId: (organizationId: number) => knex('project as p') + .where('p.organization', organizationId) + .toString(), + selectProjectByEnvironmentID: (id: number) => knex('environment as e') + .select('project.*') + .join('project', 'e.project', '=', 'project.id') + .where(knex.raw('e.id = ?', id)) + .limit(1) + .toString(), + deleteEnvironmentVariables: (id: number) => knex('env_vars') + .where('project', '=', id) + .delete() + .toString(), + deleteDeployTargetConfigs: (id: number) => knex('deploy_target_config') + .where('project', '=', id) + .delete() + .toString(), + deleteNotifications: (id: number) => knex('project_notification') + .where('pid', '=', id) + .delete() + .toString(), + deleteProject: (id: number) => knex('project') + .where('id', '=', id) + .delete() + .toString(), + selectEnvironmentsByProjectId: (id: number) => knex('environment as e') + .where('e.project', '=', id) + .andWhere('e.deleted', '0000-00-00 00:00:00') + .toString(), selectProjectByEnvironmentId: (environmentId, environmentType = []) => { - let q = knex('environment as e') + const q = knex('environment as e') .select( 'e.id', { envName: 'e.name' }, @@ -95,7 +78,7 @@ export const Sql = { 'e.project', 'e.openshift_project_name', 'p.name', - { projectId: 'p.id'}, + { projectId: 'p.id' }, 'p.organization', ) .leftJoin('project as p', 'p.id', '=', 'e.project'); @@ -107,38 +90,35 @@ export const Sql = { }, updateProject: ({ id, - patch + patch, }: { id: number; patch: { [key: string]: any }; - }) => - knex('project') - .where('id', '=', id) - .update(patch) - .toString(), - truncateProject: () => - knex('project') - .truncate() - .toString(), + }) => knex('project') + .where('id', '=', id) + .update(patch) + .toString(), + truncateProject: () => knex('project') + .truncate() + .toString(), createProject: (input) => { - const { id, name, gitUrl, - availability = "STANDARD", + availability = 'STANDARD', privateKey, subfolder, routerPattern, openshift, - branches = "true", - pullrequests = "true", + branches = 'true', + pullrequests = 'true', productionEnvironment, productionRoutes, - productionAlias = "lagoon-production", + productionAlias = 'lagoon-production', standbyProductionEnvironment, standbyRoutes, - standbyAlias = "lagoon-standby", + standbyAlias = 'lagoon-standby', autoIdle = 1, storageCalc = 1, problemsUi = 0, @@ -149,37 +129,37 @@ export const Sql = { developmentEnvironmentsLimit = 5, organization, buildImage, - sharedBaasBucket + sharedBaasBucket, } = input; return knex('project').insert({ - id, - name, - gitUrl, - availability, - privateKey, - subfolder, - routerPattern, - branches, - productionEnvironment, - productionRoutes, - productionAlias, - standbyProductionEnvironment, - standbyRoutes, - standbyAlias, - autoIdle, - problemsUi, - factsUi, - productionBuildPriority, - developmentBuildPriority, - deploymentsDisabled, - storageCalc, - pullrequests, - openshift, - developmentEnvironmentsLimit, - organization, - buildImage, - sharedBaasBucket - }).toString(); - } + id, + name, + gitUrl, + availability, + privateKey, + subfolder, + routerPattern, + branches, + productionEnvironment, + productionRoutes, + productionAlias, + standbyProductionEnvironment, + standbyRoutes, + standbyAlias, + autoIdle, + problemsUi, + factsUi, + productionBuildPriority, + developmentBuildPriority, + deploymentsDisabled, + storageCalc, + pullrequests, + openshift, + developmentEnvironmentsLimit, + organization, + buildImage, + sharedBaasBucket, + }).toString(); + }, }; diff --git a/services/api/src/resources/retentionpolicy/harbor.ts b/services/api/src/resources/retentionpolicy/harbor.ts index cda0a03db5..b900e6d673 100644 --- a/services/api/src/resources/retentionpolicy/harbor.ts +++ b/services/api/src/resources/retentionpolicy/harbor.ts @@ -1,72 +1,71 @@ -import { logger } from "../../loggers/logger"; import { query } from '../../util/db'; import { Helpers } from './helpers'; import { HarborRetentionMessage, HarborRetentionEventType, HarborRetentionMessageType } from './types'; import { Sql as environmentSql } from '../environment/sql'; import { Sql as openshiftSql } from '../openshift/sql'; import { Sql as projectSql } from '../project/sql'; -import { createRetentionPolicyTask } from '@lagoon/commons/dist/tasks'; +import { createRetentionPolicyTask } from '../../commons/tasks'; export const HarborRetentionEnforcer = () => { - const updateProjects = async (sqlClientPool, policyChanges: any) => { - // loop over all the policyChanges and get all the environments for the project, and the deploytargets environments are in - // send each deploytarget ID the policy change for the project so that the harbor in that deploytarget will - // get the updated retention policy changes immediately - for (const pol of policyChanges) { - const rows = await query(sqlClientPool, environmentSql.selectEnvironmentsByProjectId(null, pol.pid, false, false, [])); - if (rows.length > 0) { - let targets = [] - for (const row of rows) { - const deployTarget = await query(sqlClientPool, openshiftSql.selectOpenshift(row.openshift)); - if (targets.indexOf(deployTarget[0].name) === -1) { - targets.push(deployTarget[0].name); - } - } - const project = await query(sqlClientPool, projectSql.selectProjectById(pol.pid)) - for (const target of targets) { - if (pol.updatePolicy) { - const retpol = await Helpers(sqlClientPool).getRetentionPolicy(pol.rpid) - // add or update harbor policy to project in the remote cluster - const event: HarborRetentionMessage = { - type: HarborRetentionMessageType.HarborRetentionPolicy, - eventType: HarborRetentionEventType.UpdatePolicy, - data: { - project: { - id: project[0].id, - name: project[0].name, - }, - policy: JSON.parse(retpol.configuration) - } - } - const data = { - target: target, - event: event - } - await createRetentionPolicyTask({ key: 'harborpolicy:update', data }); - } - if (pol.removePolicy) { - // remove harbor policy from project in the remote cluster - const event: HarborRetentionMessage = { - type: HarborRetentionMessageType.HarborRetentionPolicy, - eventType: HarborRetentionEventType.RemovePolicy, - data: { - project: { - id: project[0].id, - name: project[0].name, - } - } - } - const data = { - target: target, - event: event - } - await createRetentionPolicyTask({ key: 'harborpolicy:update', data }); - } - } - } + const updateProjects = async (sqlClientPool, policyChanges: any) => { + // loop over all the policyChanges and get all the environments for the project, and the deploytargets environments are in + // send each deploytarget ID the policy change for the project so that the harbor in that deploytarget will + // get the updated retention policy changes immediately + for (const pol of policyChanges) { + const rows = await query(sqlClientPool, environmentSql.selectEnvironmentsByProjectId(null, pol.pid, false, false, [])); + if (rows.length > 0) { + const targets = []; + for (const row of rows) { + const deployTarget = await query(sqlClientPool, openshiftSql.selectOpenshift(row.openshift)); + if (targets.indexOf(deployTarget[0].name) === -1) { + targets.push(deployTarget[0].name); + } } + const project = await query(sqlClientPool, projectSql.selectProjectById(pol.pid)); + for (const target of targets) { + if (pol.updatePolicy) { + const retpol = await Helpers(sqlClientPool).getRetentionPolicy(pol.rpid); + // add or update harbor policy to project in the remote cluster + const event: HarborRetentionMessage = { + type: HarborRetentionMessageType.HarborRetentionPolicy, + eventType: HarborRetentionEventType.UpdatePolicy, + data: { + project: { + id: project[0].id, + name: project[0].name, + }, + policy: JSON.parse(retpol.configuration), + }, + }; + const data = { + target, + event, + }; + await createRetentionPolicyTask({ key: 'harborpolicy:update', data }); + } + if (pol.removePolicy) { + // remove harbor policy from project in the remote cluster + const event: HarborRetentionMessage = { + type: HarborRetentionMessageType.HarborRetentionPolicy, + eventType: HarborRetentionEventType.RemovePolicy, + data: { + project: { + id: project[0].id, + name: project[0].name, + }, + }, + }; + const data = { + target, + event, + }; + await createRetentionPolicyTask({ key: 'harborpolicy:update', data }); + } + } + } } - return { - updateProjects, - }; -}; \ No newline at end of file + }; + return { + updateProjects, + }; +}; diff --git a/services/api/src/resources/retentionpolicy/helpers.ts b/services/api/src/resources/retentionpolicy/helpers.ts index 78fea05e9a..ae0fe0b667 100644 --- a/services/api/src/resources/retentionpolicy/helpers.ts +++ b/services/api/src/resources/retentionpolicy/helpers.ts @@ -1,11 +1,9 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; -import { asyncPipe } from '@lagoon/commons/dist/util/func'; import { query } from '../../util/db'; import { Sql } from './sql'; import { Sql as organizationSql } from '../organization/sql'; import { Sql as projectSql } from '../project/sql'; -import { logger } from '../../loggers/logger'; import { Helpers as projectHelpers } from '../project/helpers'; import { HarborRetentionEnforcer } from './harbor'; @@ -31,65 +29,66 @@ export const Helpers = (sqlClientPool: Pool) => { return rows; }; const getRetentionPoliciesByProjectWithType = async (type: string, project: number) => { - let rows = [] + let rows = []; if (type) { - rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByTypeAndLink(type, project, "project")); + rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByTypeAndLink(type, project, 'project')); } else { - rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByLink(project, "project")); + rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByLink(project, 'project')); } return rows; }; const getRetentionPoliciesByOrganizationWithType = async (type: string, organization: number) => { - let rows = [] + let rows = []; if (type) { - rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByTypeAndLink(type, organization, "organization")); + rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByTypeAndLink(type, organization, 'organization')); } else { - rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByLink(organization, "organization")); + rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByLink(organization, 'organization')); } return rows; }; const getRetentionPoliciesByGlobalWithType = async (type: string) => { - let rows = [] + let rows = []; if (type) { - rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByTypeAndLink(type, 0, "global")); + rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByTypeAndLink(type, 0, 'global')); } else { - rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByLink(0, "global")); + rows = await query(sqlClientPool, Sql.selectRetentionPoliciesByLink(0, 'global')); } return rows; }; const getRetentionPoliciesByScopeWithTypeAndLink = async (type: string, scope: string, scopeId: number) => { - let rows, gr, or, pr, orgRows = [] + let rows; let gr; let or; let pr; let + orgRows = []; const globalRows = await getRetentionPoliciesByGlobalWithType(type); switch (scope) { - case "project": - const projectData = await projectHelpers(sqlClientPool).getProjectById(scopeId) + case 'project': + const projectData = await projectHelpers(sqlClientPool).getProjectById(scopeId); orgRows = await getRetentionPoliciesByOrganizationWithType(type, projectData.organization); const pRows = await getRetentionPoliciesByProjectWithType(type, scopeId); - gr = globalRows.map(row => ({ ...row, source: "global", configuration: {type: row.type, ...JSON.parse(row.configuration)} })) - or = orgRows.map(row => ({ ...row, source: "organization", configuration: {type: row.type, ...JSON.parse(row.configuration)} })) - pr = pRows.map(row => ({ ...row, source: "project", configuration: {type: row.type, ...JSON.parse(row.configuration)} })) - if (pr.length == 0) { - rows = gr.filter(ar => !or.find(rm => (rm.type === ar.type ) )) - if (or.length != 0) { - rows = or.filter(ar => !pr.find(rm => (rm.type === ar.type ) )) + gr = globalRows.map(row => ({ ...row, source: 'global', configuration: { type: row.type, ...JSON.parse(row.configuration) } })); + or = orgRows.map(row => ({ ...row, source: 'organization', configuration: { type: row.type, ...JSON.parse(row.configuration) } })); + pr = pRows.map(row => ({ ...row, source: 'project', configuration: { type: row.type, ...JSON.parse(row.configuration) } })); + if (pr.length === 0) { + rows = gr.filter(ar => !or.find(rm => (rm.type === ar.type))); + if (or.length !== 0) { + rows = or.filter(ar => !pr.find(rm => (rm.type === ar.type))); } } else { - return pr + return pr; } - rows.push(...pr) + rows.push(...pr); return rows; - case "organization": + case 'organization': orgRows = await getRetentionPoliciesByOrganizationWithType(type, scopeId); - gr = globalRows.map(row => ({ ...row, source: "global", configuration: {type: row.type, ...JSON.parse(row.configuration)} })) - or = orgRows.map(row => ({ ...row, source: "organization", configuration: {type: row.type, ...JSON.parse(row.configuration)} })) - rows = gr.filter(ar => !or.find(rm => (rm.type === ar.type ) )) - rows.push(...or) + gr = globalRows.map(row => ({ ...row, source: 'global', configuration: { type: row.type, ...JSON.parse(row.configuration) } })); + or = orgRows.map(row => ({ ...row, source: 'organization', configuration: { type: row.type, ...JSON.parse(row.configuration) } })); + rows = gr.filter(ar => !or.find(rm => (rm.type === ar.type))); + rows.push(...or); return rows; - case "global": - return globalRows.map(row => ({ ...row, source: "global", configuration: {type: row.type, ...JSON.parse(row.configuration)} })) + case 'global': + return globalRows.map(row => ({ ...row, source: 'global', configuration: { type: row.type, ...JSON.parse(row.configuration) } })); default: throw new Error( - `No matching scope` + 'No matching scope', ); } }; @@ -107,28 +106,28 @@ export const Helpers = (sqlClientPool: Pool) => { the data this generates should be in the format the `policyEnforcer` requires, see `policyEnforcer` for details */ const getProjectIdsForAssociatedPolicyID = async (type: string, policyId: number, removal: boolean) => { - let policyOverrides = [] // store all the policy overrides that this function will generate - let projects = [] // store all the collected project ids so that we can use it to select other projects not in this list later on + const policyOverrides = []; // store all the policy overrides that this function will generate + const projects = []; // store all the collected project ids so that we can use it to select other projects not in this list later on // this policy is applied globally, so check for any organizations or projects that may use this policy // check if any organizations have a policy that is different to this updated policy, these should be excluded from receiving any updates - const oids = await query(sqlClientPool, Sql.selectScopeIDsByRetentionPolicyTypeExcludingPolicyID(type, "organization", policyId)); - if (oids.length != 0 && oids[0]["scopeIds"] != null) { - for (const oid of oids[0]["scopeIds"].split(',')) { - const opids = await query(sqlClientPool, organizationSql.selectOrganizationProjectIds(oid)) - if (opids[0]["projectIds"] != null) { - for (const pid of opids[0]["projectIds"].split(',')) { - projects.push(pid) - const d = await getRetentionPolicyByTypeAndLink(type, oid, "organization") - if (removal && d && d.id == policyId) { - const targetIndex = policyOverrides.findIndex(f=>f.pid===pid); - const policy = {pid: pid, updatePolicy: true, rpid: d.id} - if (targetIndex != -1) { + const oids = await query(sqlClientPool, Sql.selectScopeIDsByRetentionPolicyTypeExcludingPolicyID(type, 'organization', policyId)); + if (oids.length !== 0 && oids[0].scopeIds != null) { + for (const oid of oids[0].scopeIds.split(',')) { + const opids = await query(sqlClientPool, organizationSql.selectOrganizationProjectIds(oid)); + if (opids[0].projectIds != null) { + for (const pid of opids[0].projectIds.split(',')) { + projects.push(pid); + const d = await getRetentionPolicyByTypeAndLink(type, oid, 'organization'); + if (removal && d && d.id === policyId) { + const targetIndex = policyOverrides.findIndex(f => f.pid === pid); + const policy = { pid, updatePolicy: true, rpid: d.id }; + if (targetIndex !== -1) { // if the project already exists in the overrides, but a change is determined to be made // update the project with the new policy policyOverrides[targetIndex] = policy; } else { // otherwise add the project and policy as a new item - policyOverrides.push(policy) + policyOverrides.push(policy); } } } @@ -136,88 +135,88 @@ export const Helpers = (sqlClientPool: Pool) => { } } // check if any projects have a policy that is different to this updated policy, these should be excluded from receiving any updates - const pids = await query(sqlClientPool, Sql.selectScopeIDsByRetentionPolicyTypeExcludingPolicyID(type, "project", policyId)); - if (pids.length != 0 && pids[0]["scopeIds"] != null) { - for (const pid of pids[0]["scopeIds"].split(',')) { + const pids = await query(sqlClientPool, Sql.selectScopeIDsByRetentionPolicyTypeExcludingPolicyID(type, 'project', policyId)); + if (pids.length !== 0 && pids[0].scopeIds != null) { + for (const pid of pids[0].scopeIds.split(',')) { projects.indexOf(pid) === -1 && projects.push(pid); - const d = await getRetentionPolicyByTypeAndLink(type, pid, "project") - if (removal && d && d.id == policyId) { - const targetIndex = policyOverrides.findIndex(f=>f.pid===pid); - const policy = {pid: pid, updatePolicy: true, rpid: d.id} - if (targetIndex != -1) { + const d = await getRetentionPolicyByTypeAndLink(type, pid, 'project'); + if (removal && d && d.id === policyId) { + const targetIndex = policyOverrides.findIndex(f => f.pid === pid); + const policy = { pid, updatePolicy: true, rpid: d.id }; + if (targetIndex !== -1) { // if the project already exists in the overrides, but a change is determined to be made // update the project with the new policy policyOverrides[targetIndex] = policy; } else { // otherwise add the project and policy as a new item - policyOverrides.push(policy) + policyOverrides.push(policy); } } } } // select all project ids that don't have a policy override - const updateProjects = await query(sqlClientPool, projectSql.selectAllProjectIDsNotIn(projects)) - if (updateProjects[0]["projectIds"] != null) { - const projects = updateProjects[0]["projectIds"].split(','); + const updateProjects = await query(sqlClientPool, projectSql.selectAllProjectIDsNotIn(projects)); + if (updateProjects[0].projectIds != null) { + const projects = updateProjects[0].projectIds.split(','); for (const pid of projects) { if (removal) { // if the project has no other policies to apply // then it need to have any policies that may have been attached to it, removed from it // set that here for the policyEnforcer to consume - policyOverrides.push({pid: pid, removePolicy: true}) + policyOverrides.push({ pid, removePolicy: true }); } else { - policyOverrides.push({pid: pid, updatePolicy: true, rpid: policyId}) + policyOverrides.push({ pid, updatePolicy: true, rpid: policyId }); } } } // all of these project ids should get an update as long as the policy type requires immediate update changes - return policyOverrides - } + return policyOverrides; + }; /* getRetentionPolicyChangesRequiredByScope generates a list of project ids and the associated policy id that should be attached to this project the data this generates should be in the format the `policyEnforcer` requires, see `policyEnforcer` for details */ const getRetentionPolicyChangesRequired = async (scopeId: number, scope: string, type: string, policyId: number, removal: boolean) => { - const globPols = await getRetentionPoliciesByScopeWithTypeAndLink(type, "global", 0) - let policyOverrides = [] // projects with override policies + const globPols = await getRetentionPoliciesByScopeWithTypeAndLink(type, 'global', 0); + const policyOverrides = []; // projects with override policies switch (scope) { - case "global": - const projects = await getProjectIdsForAssociatedPolicyID(type, policyId, removal) + case 'global': + const projects = await getProjectIdsForAssociatedPolicyID(type, policyId, removal); for (const p of projects) { - policyOverrides.push(p) + policyOverrides.push(p); } break; - case "organization": - const orgProjects = await query(sqlClientPool, organizationSql.selectOrganizationProjects(scopeId)) - let skip = false + case 'organization': + const orgProjects = await query(sqlClientPool, organizationSql.selectOrganizationProjects(scopeId)); + let skip = false; for (const p of orgProjects) { - const pRetPols = await getRetentionPoliciesByScopeWithTypeAndLink(type, "project", p.id) + const pRetPols = await getRetentionPoliciesByScopeWithTypeAndLink(type, 'project', p.id); for (const rp of pRetPols) { - skip = true + skip = true; switch (rp.source) { - case "global": + case 'global': // if this policy is being removed from an organization, and there is a global policy that can be applied // set that here if (removal) { if (rp.configuration.enabled) { - policyOverrides.push({pid: p.id, updatePolicy: true, rpid: rp.id}) + policyOverrides.push({ pid: p.id, updatePolicy: true, rpid: rp.id }); } else { - policyOverrides.push({pid: p.id, removePolicy: true}) + policyOverrides.push({ pid: p.id, removePolicy: true }); } } break; - case "organization": + case 'organization': // if this policy is being added to an organization, and there is a an organization policy that can be applied // set that here if (!removal) { if (rp.configuration.enabled) { - policyOverrides.push({pid: p.id, updatePolicy: true, rpid: rp.id}) + policyOverrides.push({ pid: p.id, updatePolicy: true, rpid: rp.id }); } else { - policyOverrides.push({pid: p.id, removePolicy: true}) + policyOverrides.push({ pid: p.id, removePolicy: true }); } } break; - case "project": + case 'project': // do nothing if the project has an override for the project, as it takes precedence break; } @@ -226,59 +225,55 @@ export const Helpers = (sqlClientPool: Pool) => { // if the project has no other policies to apply // then it need to have any policies that may have been attached to it, removed from it // set that here for the policyEnforcer to consume - policyOverrides.push({pid: p.id, removePolicy: true}) + policyOverrides.push({ pid: p.id, removePolicy: true }); } - skip = false + skip = false; } break; - case "project": - let policyToApply = null - const projectData = await projectHelpers(sqlClientPool).getProjectById(scopeId) + case 'project': + let policyToApply = null; + const projectData = await projectHelpers(sqlClientPool).getProjectById(scopeId); const orgRows = await getRetentionPoliciesByOrganizationWithType(type, projectData.organization); - const pRetPols = await getRetentionPoliciesByScopeWithTypeAndLink(type, "project", scopeId) - if (pRetPols.length == 1) { - policyToApply = pRetPols[0] - } else { - if (orgRows.length == 1) { - policyToApply = orgRows[0] - } else { - if (globPols.length == 1) { - // apply the global polcy - policyToApply = globPols[0] - } - } + const pRetPols = await getRetentionPoliciesByScopeWithTypeAndLink(type, 'project', scopeId); + if (pRetPols.length === 1) { + policyToApply = pRetPols[0]; + } else if (orgRows.length === 1) { + policyToApply = orgRows[0]; + } else if (globPols.length === 1) { + // apply the global polcy + policyToApply = globPols[0]; } // if there is a policy to apply, and it is enabled, enable it here if (policyToApply && policyToApply.configuration.enabled) { - policyOverrides.push({pid: scopeId, updatePolicy: true, rpid: policyToApply.id}) + policyOverrides.push({ pid: scopeId, updatePolicy: true, rpid: policyToApply.id }); } else { // if the project has no other policies to apply // then it need to have any policies that may have been attached to it, removed from it // set that here for the policyEnforcer to consume - policyOverrides.push({pid: scopeId, removePolicy: true}) + policyOverrides.push({ pid: scopeId, removePolicy: true }); } break; default: throw new Error( - `No matching scope` + 'No matching scope', ); } - return policyOverrides - } + return policyOverrides; + }; const postRetentionPolicyUpdateHook = async (type: string, policyId: number, policyChanges: any, removal: boolean = false) => { // retrieve all projects that need to be updated if a change in the policy is made // not all policies will require immediate updates, but those that do will be done here if (!policyChanges) { - policyChanges = await getProjectIdsForAssociatedPolicyID(type, policyId, removal) + policyChanges = await getProjectIdsForAssociatedPolicyID(type, policyId, removal); } - await policyEnforcer(policyChanges, type) - } + await policyEnforcer(policyChanges, type); + }; // this hook can be used to perform actions when a policy is added to or removed from a scope // depending on the scope const postRetentionPolicyLinkHook = async (scopeId: number, scope: string, type: string, policyId: number, removal: boolean = false) => { - const policyChanges = await getRetentionPolicyChangesRequired(scopeId, scope, type, policyId, removal) - await policyEnforcer(policyChanges, type) - } + const policyChanges = await getRetentionPolicyChangesRequired(scopeId, scope, type, policyId, removal); + await policyEnforcer(policyChanges, type); + }; /* policyEnforcer is the actual policy enforcement function, it will handle execution of policy changes that are required, if they are required. the payload of `policyChanges` is as follows @@ -291,41 +286,41 @@ export const Helpers = (sqlClientPool: Pool) => { the post retention hooks (postRetentionPolicyLinkHook and postRetentionPolicyUpdateHook) will call policyEnforcer based on which resolver called the hook (addRetentionPolicyLink, updateRetentionPolicy, removeRetentionPolicyLink) */ - const policyEnforcer =async (policyChanges: any, type: string) => { + const policyEnforcer = async (policyChanges: any, type: string) => { switch (type) { - case "harbor": + case 'harbor': // send this to the harbor retention policy enforcer - await HarborRetentionEnforcer().updateProjects(sqlClientPool, policyChanges) + await HarborRetentionEnforcer().updateProjects(sqlClientPool, policyChanges); break; - case "history": + case 'history': // do nothing, history changes are executed when deployment or task history is modified // so policy updates are implemented in realtime break; default: throw new Error( - `No matching type` + 'No matching type', ); } - } + }; // this hook can be used after a deployment has been updated to perform changes to any retention policies as required - const postDeploymentProjectPolicyHook = async (projectId: number, status: string ) => { + const postDeploymentProjectPolicyHook = async (projectId: number, status: string) => { switch (status) { case 'complete': case 'failed': case 'cancelled': - const rows = await getRetentionPoliciesByScopeWithTypeAndLink("harbor", "project", projectId); + const rows = await getRetentionPoliciesByScopeWithTypeAndLink('harbor', 'project', projectId); if (rows[0]) { // if a deployment is complete, cancelled, or fails, run the postretentionpolicylinkhook so that // any harbor policies are applied to the new environment or project as required // this is done just in case the project or environment was created AFTER the policy was created to ensure that it gets any updates // additionally, it happens here rather than at project creation as there may be no harbor project at the time the project is created - await postRetentionPolicyLinkHook(projectId, "project", rows[0].type, rows[0].id, !rows[0].configuration.enabled) + await postRetentionPolicyLinkHook(projectId, 'project', rows[0].type, rows[0].id, !rows[0].configuration.enabled); } break; default: break; } - } + }; return { getRetentionPolicy, getRetentionPolicyByName, @@ -347,46 +342,46 @@ export const Helpers = (sqlClientPool: Pool) => { deleteRetentionPolicy: async (id: number) => { // check for globals with this retention policy const globals = await query( - sqlClientPool, Sql.selectRetentionPoliciesByLinkAndPolicyID(id, "global") + sqlClientPool, Sql.selectRetentionPoliciesByLinkAndPolicyID(id, 'global'), ); if (globals.length > 0) { throw new Error( - 'Unable to delete retention policy, it is in use globally and should be removed from global consumption first' + 'Unable to delete retention policy, it is in use globally and should be removed from global consumption first', ); } // check for organizations with this retention policy const orgs = await query( - sqlClientPool, Sql.selectRetentionPoliciesByLinkAndPolicyID(id, "organization") + sqlClientPool, Sql.selectRetentionPoliciesByLinkAndPolicyID(id, 'organization'), ); if (orgs.length > 0) { throw new Error( - 'Unable to delete retention policy, there are organizations using it that should be removed from first' + 'Unable to delete retention policy, there are organizations using it that should be removed from first', ); } // check for organizations with this retention policy const projects = await query( - sqlClientPool, Sql.selectRetentionPoliciesByLinkAndPolicyID(id, "project") + sqlClientPool, Sql.selectRetentionPoliciesByLinkAndPolicyID(id, 'project'), ); if (projects.length > 0) { throw new Error( - 'Unable to delete retention policy, there are projects using it that should be removed from first' + 'Unable to delete retention policy, there are projects using it that should be removed from first', ); } await query( sqlClientPool, - Sql.deleteRetentionPolicy(id) + Sql.deleteRetentionPolicy(id), ); }, updateRetentionPolicy: async (id: number, patch: any) => { await query( sqlClientPool, Sql.updateRetentionPolicy({ - id: id, - patch: patch - }) + id, + patch, + }), ); - } + }, }; }; diff --git a/services/api/src/resources/retentionpolicy/history.ts b/services/api/src/resources/retentionpolicy/history.ts index 8e02e6a74f..a12b8eca3e 100644 --- a/services/api/src/resources/retentionpolicy/history.ts +++ b/services/api/src/resources/retentionpolicy/history.ts @@ -1,5 +1,3 @@ - - import { Helpers } from './helpers'; import { sqlClientPool } from '../../clients/sqlClient'; import { Sql as deploymentSql } from '../deployment/sql'; @@ -9,231 +7,230 @@ import { Sql as environmentSql } from '../environment/sql'; import { sendToLagoonActions, // @ts-ignore -} from '@lagoon/commons/dist/tasks'; +} from '../../commons/tasks'; import { query } from '../../util/db'; -import { logger } from '../../loggers/logger'; export const HistoryRetentionEnforcer = () => { - const cleanupTask = async (projectData: any, environmentData: any, task: any) => { - // clean up the task log history and associated files from S3 - const actionData = { - type: "retentionCleanup", - eventType: "taskCleanup", - data: { - environmentName: environmentData.name, - environmentId: environmentData.id, - projectName: projectData.name, - projectId: projectData.id, - task: { - id: task.id.toString(), - }, - remoteId: task.remoteId, - } - } - sendToLagoonActions("retentionCleanup", actionData) - } - const cleanupTasks = async (projectData: any, environmentData: any) => { - // basic clean up all but X latest tasks - const retpol = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink("history", "project", projectData.id) - if (retpol.length > 0) { - const c = retpol[0].configuration - if (c.enabled) { - let historyToDelete = [] - switch (c.taskType) { - case "count": - historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryRetention(environmentData.id, c.taskHistory)); - break; - case "days": - historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryRetentionDays(environmentData.id, c.taskHistory)); - break; - case "months": - historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryRetentionMonths(environmentData.id, c.taskHistory)); - break; - } - for (const r of historyToDelete) { - // fire off message to action-handler service to proceed with cleaning up old data in buckets - const actionData = { - type: "retentionCleanup", - eventType: "taskCleanup", - data: { - environmentName: environmentData.name, - environmentId: environmentData.id, - projectName: projectData.name, - projectId: projectData.id, - task: { - id: r.id.toString(), - }, - remoteId: r.remoteId, - } - } - sendToLagoonActions("retentionCleanup", actionData) - } - if (historyToDelete.length != 0) { - switch (c.taskType) { - case "count": - await query(sqlClientPool, taskSql.deleteTaskHistory(environmentData.id, c.taskHistory)); - break; - case "days": - await query(sqlClientPool, taskSql.deleteTaskHistoryDays(environmentData.id, c.taskHistory)); - break; - case "months": - await query(sqlClientPool, taskSql.deleteTaskHistoryMonths(environmentData.id, c.taskHistory)); - break; - } - } - } + const cleanupTask = async (projectData: any, environmentData: any, task: any) => { + // clean up the task log history and associated files from S3 + const actionData = { + type: 'retentionCleanup', + eventType: 'taskCleanup', + data: { + environmentName: environmentData.name, + environmentId: environmentData.id, + projectName: projectData.name, + projectId: projectData.id, + task: { + id: task.id.toString(), + }, + remoteId: task.remoteId, + }, + }; + sendToLagoonActions('retentionCleanup', actionData); + }; + const cleanupTasks = async (projectData: any, environmentData: any) => { + // basic clean up all but X latest tasks + const retpol = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink('history', 'project', projectData.id); + if (retpol.length > 0) { + const c = retpol[0].configuration; + if (c.enabled) { + let historyToDelete = []; + switch (c.taskType) { + case 'count': + historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryRetention(environmentData.id, c.taskHistory)); + break; + case 'days': + historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryRetentionDays(environmentData.id, c.taskHistory)); + break; + case 'months': + historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryRetentionMonths(environmentData.id, c.taskHistory)); + break; } - } - const cleanupDeployment = async (projectData: any, environmentData: any, deployment: any) => { - // clean up the deployment log history and associated files from S3 - const actionData = { - type: "retentionCleanup", - eventType: "buildCleanup", + for (const r of historyToDelete) { + // fire off message to action-handler service to proceed with cleaning up old data in buckets + const actionData = { + type: 'retentionCleanup', + eventType: 'taskCleanup', data: { - environmentName: environmentData.name, - projectName: projectData.name, - environmentId: environmentData.id, - projectId: projectData.id, - buildName: deployment.name, - remoteId: deployment.remoteId, - } + environmentName: environmentData.name, + environmentId: environmentData.id, + projectName: projectData.name, + projectId: projectData.id, + task: { + id: r.id.toString(), + }, + remoteId: r.remoteId, + }, + }; + sendToLagoonActions('retentionCleanup', actionData); } - sendToLagoonActions("retentionCleanup", actionData) - } - const cleanupDeployments = async (projectData: any, environmentData: any) => { - // basic clean up all but X latest tasks - const retpol = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink("history", "project", projectData.id) - if (retpol.length > 0) { - const c = retpol[0].configuration - if (c.enabled) { - let historyToDelete = [] - switch (c.deploymentType) { - case "count": - historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryRetention(environmentData.id, c.deploymentHistory)); - break; - case "days": - historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryRetentionDays(environmentData.id, c.deploymentHistory)); - break; - case "months": - historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryRetentionMonths(environmentData.id, c.deploymentHistory)); - break; - } - for (const r of historyToDelete) { - // fire off message to action-handler service to proceed with cleaning up old data in buckets - const actionData = { - type: "retentionCleanup", - eventType: "buildCleanup", - data: { - environmentName: environmentData.name, - projectName: projectData.name, - environmentId: environmentData.id, - projectId: projectData.id, - buildName: r.name, - remoteId: r.remoteId, - } - } - sendToLagoonActions("retentionCleanup", actionData) - } - if (historyToDelete.length != 0) { - switch (c.deploymentType) { - case "count": - await query(sqlClientPool, deploymentSql.deleteDeploymentHistory(environmentData.id, c.deploymentHistory)); - break; - case "days": - await query(sqlClientPool, deploymentSql.deleteDeploymentHistoryDays(environmentData.id, c.deploymentHistory)); - break; - case "months": - await query(sqlClientPool, deploymentSql.deleteDeploymentHistoryMonths(environmentData.id, c.deploymentHistory)); - break; - } - } - } + if (historyToDelete.length !== 0) { + switch (c.taskType) { + case 'count': + await query(sqlClientPool, taskSql.deleteTaskHistory(environmentData.id, c.taskHistory)); + break; + case 'days': + await query(sqlClientPool, taskSql.deleteTaskHistoryDays(environmentData.id, c.taskHistory)); + break; + case 'months': + await query(sqlClientPool, taskSql.deleteTaskHistoryMonths(environmentData.id, c.taskHistory)); + break; + } } + } } - const cleanupAllDeployments = async (projectData: any, environmentData: any) => { - // get all the environment deployment history - const historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryForEnvironment(environmentData.id)); - for (const r of historyToDelete) { - // fire off message to action-handler service to proceed with cleaning up old data in buckets - const actionData = { - type: "retentionCleanup", - eventType: "buildCleanup", - data: { - environmentName: environmentData.name, - projectName: projectData.name, - environmentId: environmentData.id, - projectId: projectData.id, - buildName: r.name, - remoteId: r.remoteId, - } - } - sendToLagoonActions("retentionCleanup", actionData) - } - if (historyToDelete.length != 0) { - // delete all the environment deployment history - await query(sqlClientPool, deploymentSql.deleteDeploymentHistoryForEnvironment(environmentData.id)); + }; + const cleanupDeployment = async (projectData: any, environmentData: any, deployment: any) => { + // clean up the deployment log history and associated files from S3 + const actionData = { + type: 'retentionCleanup', + eventType: 'buildCleanup', + data: { + environmentName: environmentData.name, + projectName: projectData.name, + environmentId: environmentData.id, + projectId: projectData.id, + buildName: deployment.name, + remoteId: deployment.remoteId, + }, + }; + sendToLagoonActions('retentionCleanup', actionData); + }; + const cleanupDeployments = async (projectData: any, environmentData: any) => { + // basic clean up all but X latest tasks + const retpol = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink('history', 'project', projectData.id); + if (retpol.length > 0) { + const c = retpol[0].configuration; + if (c.enabled) { + let historyToDelete = []; + switch (c.deploymentType) { + case 'count': + historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryRetention(environmentData.id, c.deploymentHistory)); + break; + case 'days': + historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryRetentionDays(environmentData.id, c.deploymentHistory)); + break; + case 'months': + historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryRetentionMonths(environmentData.id, c.deploymentHistory)); + break; } - } - const cleanupAllTasks = async (projectData: any, environmentData: any) => { - // get all the environment task history - const historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryForEnvironment(environmentData.id)); for (const r of historyToDelete) { - // fire off message to action-handler service to proceed with cleaning up old data in buckets - const actionData = { - type: "retentionCleanup", - eventType: "buildCleanup", - data: { - environmentName: environmentData.name, - projectName: projectData.name, - environmentId: environmentData.id, - projectId: projectData.id, - buildName: r.name, - remoteId: r.remoteId, - } - } - sendToLagoonActions("retentionCleanup", actionData) + // fire off message to action-handler service to proceed with cleaning up old data in buckets + const actionData = { + type: 'retentionCleanup', + eventType: 'buildCleanup', + data: { + environmentName: environmentData.name, + projectName: projectData.name, + environmentId: environmentData.id, + projectId: projectData.id, + buildName: r.name, + remoteId: r.remoteId, + }, + }; + sendToLagoonActions('retentionCleanup', actionData); } - if (historyToDelete.length != 0) { - // delete all the environment task history - await query(sqlClientPool, taskSql.deleteTaskHistoryForEnvironment(environmentData.id)); + if (historyToDelete.length !== 0) { + switch (c.deploymentType) { + case 'count': + await query(sqlClientPool, deploymentSql.deleteDeploymentHistory(environmentData.id, c.deploymentHistory)); + break; + case 'days': + await query(sqlClientPool, deploymentSql.deleteDeploymentHistoryDays(environmentData.id, c.deploymentHistory)); + break; + case 'months': + await query(sqlClientPool, deploymentSql.deleteDeploymentHistoryMonths(environmentData.id, c.deploymentHistory)); + break; + } } + } } - const saveEnvironmentHistoryBeforeDeletion = async (projectData: any, environmentData: any) => { - // ENABLE_SAVED_HISTORY_EXPORT will save the deployment and task history if set to true - // this is a way to export a full copy of the environment data (id, name, created, deleted etc..), the project, and the task/deployment/backup/storage history - // this is a JSON payload that could later be consumed for historical purposes - // by default this feature is ENABLED - // the deleted data ends up in the lagoon files bucket in a directory called history - // the format of the path is history/{projectname}-{projectid}/{environmentname}-{environmentid}/history-{deletedunixtimestamp}.json - const ENABLE_SAVED_HISTORY_EXPORT = process.env.ENABLE_SAVED_HISTORY_EXPORT || "true" - if (ENABLE_SAVED_HISTORY_EXPORT == "true") { - const taskHistory = await query(sqlClientPool, taskSql.selectTaskHistoryForEnvironment(environmentData.id)); - const deploymentHistory = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryForEnvironment(environmentData.id)); - const backupHistory = await query(sqlClientPool, backupSql.selectBackupsByEnvironmentId(environmentData.id)); - const environmentStorage = await query(sqlClientPool, environmentSql.selectEnvironmentStorageByEnvironmentId(environmentData.id)); - delete projectData["privateKey"] // remove the privatekey from the project payload before saving the history - const actionData = { - type: "retentionHistory", - eventType: "saveHistory", - data: { - environment: environmentData, - project: projectData, - tasks: taskHistory, - deployments: deploymentHistory, - backups: backupHistory, - storage: environmentStorage, - } - } - sendToLagoonActions("retentionHistory", actionData) - } + }; + const cleanupAllDeployments = async (projectData: any, environmentData: any) => { + // get all the environment deployment history + const historyToDelete = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryForEnvironment(environmentData.id)); + for (const r of historyToDelete) { + // fire off message to action-handler service to proceed with cleaning up old data in buckets + const actionData = { + type: 'retentionCleanup', + eventType: 'buildCleanup', + data: { + environmentName: environmentData.name, + projectName: projectData.name, + environmentId: environmentData.id, + projectId: projectData.id, + buildName: r.name, + remoteId: r.remoteId, + }, + }; + sendToLagoonActions('retentionCleanup', actionData); } - return { - cleanupDeployment, - cleanupDeployments, - cleanupTask, - cleanupTasks, - cleanupAllDeployments, - cleanupAllTasks, - saveEnvironmentHistoryBeforeDeletion, - }; -}; \ No newline at end of file + if (historyToDelete.length !== 0) { + // delete all the environment deployment history + await query(sqlClientPool, deploymentSql.deleteDeploymentHistoryForEnvironment(environmentData.id)); + } + }; + const cleanupAllTasks = async (projectData: any, environmentData: any) => { + // get all the environment task history + const historyToDelete = await query(sqlClientPool, taskSql.selectTaskHistoryForEnvironment(environmentData.id)); + for (const r of historyToDelete) { + // fire off message to action-handler service to proceed with cleaning up old data in buckets + const actionData = { + type: 'retentionCleanup', + eventType: 'buildCleanup', + data: { + environmentName: environmentData.name, + projectName: projectData.name, + environmentId: environmentData.id, + projectId: projectData.id, + buildName: r.name, + remoteId: r.remoteId, + }, + }; + sendToLagoonActions('retentionCleanup', actionData); + } + if (historyToDelete.length !== 0) { + // delete all the environment task history + await query(sqlClientPool, taskSql.deleteTaskHistoryForEnvironment(environmentData.id)); + } + }; + const saveEnvironmentHistoryBeforeDeletion = async (projectData: any, environmentData: any) => { + // ENABLE_SAVED_HISTORY_EXPORT will save the deployment and task history if set to true + // this is a way to export a full copy of the environment data (id, name, created, deleted etc..), the project, and the task/deployment/backup/storage history + // this is a JSON payload that could later be consumed for historical purposes + // by default this feature is ENABLED + // the deleted data ends up in the lagoon files bucket in a directory called history + // the format of the path is history/{projectname}-{projectid}/{environmentname}-{environmentid}/history-{deletedunixtimestamp}.json + const ENABLE_SAVED_HISTORY_EXPORT = process.env.ENABLE_SAVED_HISTORY_EXPORT || 'true'; + if (ENABLE_SAVED_HISTORY_EXPORT === 'true') { + const taskHistory = await query(sqlClientPool, taskSql.selectTaskHistoryForEnvironment(environmentData.id)); + const deploymentHistory = await query(sqlClientPool, deploymentSql.selectDeploymentHistoryForEnvironment(environmentData.id)); + const backupHistory = await query(sqlClientPool, backupSql.selectBackupsByEnvironmentId(environmentData.id)); + const environmentStorage = await query(sqlClientPool, environmentSql.selectEnvironmentStorageByEnvironmentId(environmentData.id)); + delete projectData.privateKey; // remove the privatekey from the project payload before saving the history + const actionData = { + type: 'retentionHistory', + eventType: 'saveHistory', + data: { + environment: environmentData, + project: projectData, + tasks: taskHistory, + deployments: deploymentHistory, + backups: backupHistory, + storage: environmentStorage, + }, + }; + sendToLagoonActions('retentionHistory', actionData); + } + }; + return { + cleanupDeployment, + cleanupDeployments, + cleanupTask, + cleanupTasks, + cleanupAllDeployments, + cleanupAllTasks, + saveEnvironmentHistoryBeforeDeletion, + }; +}; diff --git a/services/api/src/resources/retentionpolicy/resolvers.ts b/services/api/src/resources/retentionpolicy/resolvers.ts index 9c7c4a2258..a86c350988 100644 --- a/services/api/src/resources/retentionpolicy/resolvers.ts +++ b/services/api/src/resources/retentionpolicy/resolvers.ts @@ -1,6 +1,4 @@ - import { ResolverFn } from '..'; -import { logger } from '../../loggers/logger'; import { isPatchEmpty, query, knex } from '../../util/db'; import { Helpers } from './helpers'; import { RetentionPolicy } from './types'; @@ -11,75 +9,71 @@ import { Sql } from './sql'; const createRetentionPolicy = async (sqlClientPool, hasPermission, userActivityLogger, input, type) => { await hasPermission('retention_policy', 'add'); - const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(input.name, type) + const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(input.name, type); if (retpol) { throw new Error( - `Retention policy with name ${input.name} already exists` + `Retention policy with name ${input.name} already exists`, ); } // convert the type to the configuration json on import after passing through the validator - let event + let event; try { switch (type) { - case "harbor": - event = 'api:createHarborRetentionPolicy' - input.configuration = await RetentionPolicy().returnValidatedHarborConfiguration(input) + case 'harbor': + event = 'api:createHarborRetentionPolicy'; + input.configuration = await RetentionPolicy().returnValidatedHarborConfiguration(input); break; - case "history": - event = 'api:createHistoryRetentionPolicy' - input.configuration = await RetentionPolicy().returnValidatedHistoryConfiguration(input) + case 'history': + event = 'api:createHistoryRetentionPolicy'; + input.configuration = await RetentionPolicy().returnValidatedHistoryConfiguration(input); break; default: throw new Error( - `No matching type` + 'No matching type', ); } } catch (e) { throw new Error( - `${e}` + `${e}`, ); } const { insertId } = await query( sqlClientPool, Sql.createRetentionPolicy({ - type: type, - ...input, - })); + type, + ...input, + })); const row = await Helpers(sqlClientPool).getRetentionPolicy(insertId); userActivityLogger(`User created a ${type} retention policy`, { project: '', - event: event, + event, payload: { patch: { name: input.name, configuration: input.configuration, }, - data: row - } + data: row, + }, }); - return { ...row, configuration: {type: row.type, ...JSON.parse(row.configuration)} }; -} + return { ...row, configuration: { type: row.type, ...JSON.parse(row.configuration) } }; +}; export const createHarborRetentionPolicy: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await createRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor'); -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await createRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor'); export const createHistoryRetentionPolicy: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await createRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'history'); -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await createRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'history'); const updateRetentionPolicy = async (sqlClientPool, hasPermission, userActivityLogger, input, type) => { await hasPermission('retention_policy', 'update'); @@ -88,37 +82,37 @@ const updateRetentionPolicy = async (sqlClientPool, hasPermission, userActivityL throw new Error('input.patch requires at least 1 attribute'); } - const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(input.name, type) + const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(input.name, type); if (!retpol) { throw new Error( - `Retention policy does not exist` + 'Retention policy does not exist', ); } - let patch = { - name: input.patch.name - } + const patch: Record = { + name: input.patch.name, + }; // convert the type to the configuration json on import after passing through the validator - let event + let event; try { switch (type) { - case "harbor": - event = 'api:updateHarborRetentionPolicy' - patch["configuration"] = await RetentionPolicy().returnValidatedHarborConfiguration(input.patch) + case 'harbor': + event = 'api:updateHarborRetentionPolicy'; + patch.configuration = await RetentionPolicy().returnValidatedHarborConfiguration(input.patch); break; - case "history": - event = 'api:updateHistoryRetentionPolicy' - patch["configuration"] = await RetentionPolicy().returnValidatedHistoryConfiguration(input.patch) + case 'history': + event = 'api:updateHistoryRetentionPolicy'; + patch.configuration = await RetentionPolicy().returnValidatedHistoryConfiguration(input.patch); break; default: throw new Error( - `No matching type` + 'No matching type', ); } } catch (e) { throw new Error( - `${e}` + `${e}`, ); } @@ -128,61 +122,57 @@ const updateRetentionPolicy = async (sqlClientPool, hasPermission, userActivityL userActivityLogger(`User updated ${type} retention policy`, { project: '', - event: event, + event, payload: { - patch: patch, - data: row - } + patch, + data: row, + }, }); - if (retpol.configuration != row.configuration) { + if (retpol.configuration !== row.configuration) { // if a policy is updated, and the configuration is not the same as before the update // then run postRetentionPolicyUpdateHook to make sure that the policy enforcer does // any policy updates for any impacted projects - const policyEnabled = input.patch.enabled - await Helpers(sqlClientPool).postRetentionPolicyUpdateHook(retpol.type, retpol.id, null, !policyEnabled) + const policyEnabled = input.patch.enabled; + await Helpers(sqlClientPool).postRetentionPolicyUpdateHook(retpol.type, retpol.id, null, !policyEnabled); } - return { ...row, configuration: {type: row.type, ...JSON.parse(row.configuration)} }; -} + return { ...row, configuration: { type: row.type, ...JSON.parse(row.configuration) } }; +}; export const updateHarborRetentionPolicy: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await updateRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor'); -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await updateRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor'); export const updateHistoryRetentionPolicy: ResolverFn = async ( root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await updateRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'history'); -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await updateRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, input, 'history'); const deleteRetentionPolicy = async (sqlClientPool, hasPermission, userActivityLogger, name, type) => { await hasPermission('retention_policy', 'delete'); - const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(name, type) + const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(name, type); if (!retpol) { throw new Error( - `Retention policy does not exist` + 'Retention policy does not exist', ); } - let event + let event; switch (type) { - case "harbor": - event = 'api:deleteHarborRetentionPolicy' + case 'harbor': + event = 'api:deleteHarborRetentionPolicy'; break; - case "history": - event = 'api:deleteHistoryRetentionPolicy' + case 'history': + event = 'api:deleteHistoryRetentionPolicy'; break; default: throw new Error( - `No matching type` + 'No matching type', ); } @@ -190,37 +180,33 @@ const deleteRetentionPolicy = async (sqlClientPool, hasPermission, userActivityL userActivityLogger(`User deleted a ${type} retention policy '${retpol.name}'`, { project: '', - event: event, + event, payload: { input: { - retentionPolicy: retpol.id - } - } + retentionPolicy: retpol.id, + }, + }, }); return 'success'; -} +}; export const deleteHarborRetentionPolicy: ResolverFn = async ( _root, { name }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await deleteRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, name, 'harbor'); -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await deleteRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, name, 'harbor'); export const deleteHistoryRetentionPolicy: ResolverFn = async ( _root, { name }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await deleteRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, name, 'history'); -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await deleteRetentionPolicy(sqlClientPool, hasPermission, userActivityLogger, name, 'history'); export const listAllRetentionPolicies: ResolverFn = async ( root, { name, type }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { await hasPermission('retention_policy', 'viewAll'); @@ -235,64 +221,65 @@ export const listAllRetentionPolicies: ResolverFn = async ( } const rows = await query(sqlClientPool, queryBuilder.toString()); - return rows.map(row => ({ ...row, source: null, configuration: {type: row.type, ...JSON.parse(row.configuration)} })); + return rows.map(row => ({ ...row, source: null, configuration: { type: row.type, ...JSON.parse(row.configuration) } })); }; const addRetentionPolicyLink = async (sqlClientPool, hasPermission, userActivityLogger, input, type) => { - let scopeId = 0 - let event, prefix + let scopeId = 0; + let event; let + prefix; switch (type) { - case "harbor": - prefix = 'api:addHarbor' + case 'harbor': + prefix = 'api:addHarbor'; break; - case "history": - prefix = 'api:addHistory' + case 'history': + prefix = 'api:addHistory'; break; } switch (input.scope) { - case "global": + case 'global': await hasPermission('retention_policy', `add:${type}:global`); - event = `${prefix}RetentionPolicyGlobal` + event = `${prefix}RetentionPolicyGlobal`; break; - case "organization": - const organization = await organizationHelpers(sqlClientPool).getOrganizationByName(input.scopeName) + case 'organization': + const organization = await organizationHelpers(sqlClientPool).getOrganizationByName(input.scopeName); if (!organization) { throw new Error( - `Organization does not exist` + 'Organization does not exist', ); } await hasPermission('retention_policy', `add:${type}:organization`); - scopeId = organization.id - event = `${prefix}RetentionPolicyOrganization` + scopeId = organization.id; + event = `${prefix}RetentionPolicyOrganization`; break; - case "project": - const project = await projectHelpers(sqlClientPool).getProjectByProjectInput({name: input.scopeName}) + case 'project': + const project = await projectHelpers(sqlClientPool).getProjectByProjectInput({ name: input.scopeName }); if (!project) { throw new Error( - `Project does not exist` + 'Project does not exist', ); } await hasPermission('retention_policy', `add:${type}:project`); - scopeId = project.id - event = `${prefix}RetentionPolicyProject` + scopeId = project.id; + event = `${prefix}RetentionPolicyProject`; break; default: throw new Error( - `No matching scope` + 'No matching scope', ); } - const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(input.name, type) + const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(input.name, type); if (!retpol) { throw new Error( - `Retention policy does not exist` + 'Retention policy does not exist', ); } const retpoltypes = await Helpers(sqlClientPool).getRetentionPolicyByTypeAndLink(retpol.type, scopeId, input.scope); if (retpoltypes) { throw new Error( - `A retention policy of type ${retpol.type} is already attached to the ${input.scope}` + `A retention policy of type ${retpol.type} is already attached to the ${input.scope}`, ); } @@ -302,109 +289,106 @@ const addRetentionPolicyLink = async (sqlClientPool, hasPermission, userActivity retpol.id, input.scope, scopeId, - ) + ), ); // if a policy is linked to a scope (global, organization, project) // then run postRetentionPolicyLinkHook to make sure that the policy enforcer does // any policy updates for any impacted projects - await Helpers(sqlClientPool).postRetentionPolicyLinkHook(scopeId, input.scope, retpol.type, retpol.id, false) + await Helpers(sqlClientPool).postRetentionPolicyLinkHook(scopeId, input.scope, retpol.type, retpol.id, false); userActivityLogger(`User added a ${type} retention policy '${retpol.name}' to ${input.scope}`, { project: '', - event: event, + event, payload: { input: { retentionPolicy: retpol.id, scope: input.scope, - scopeId: scopeId - } - } + scopeId, + }, + }, }); - const row = await Helpers(sqlClientPool).getRetentionPolicy(retpol.id) - return { ...row, configuration: {type: row.type, ...JSON.parse(row.configuration)} }; -} + const row = await Helpers(sqlClientPool).getRetentionPolicy(retpol.id); + return { ...row, configuration: { type: row.type, ...JSON.parse(row.configuration) } }; +}; export const addHarborRetentionPolicyLink: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await addRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor') -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await addRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor'); export const addHistoryRetentionPolicyLink: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await addRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'history') -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await addRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'history'); const removeRetentionPolicyLink = async (sqlClientPool, hasPermission, userActivityLogger, input, type) => { - let scopeId = 0 - let event, prefix + let scopeId = 0; + let event; let + prefix; switch (type) { - case "harbor": - prefix = 'api:removeHarbor' + case 'harbor': + prefix = 'api:removeHarbor'; break; - case "history": - prefix = 'api:removeHistory' + case 'history': + prefix = 'api:removeHistory'; break; } switch (input.scope) { - case "global": + case 'global': await hasPermission('retention_policy', `remove:${type}:global`); - event = `${prefix}RetentionPolicyGlobal` + event = `${prefix}RetentionPolicyGlobal`; break; - case "organization": - const organization = await organizationHelpers(sqlClientPool).getOrganizationByName(input.scopeName) + case 'organization': + const organization = await organizationHelpers(sqlClientPool).getOrganizationByName(input.scopeName); if (!organization) { throw new Error( - `Organization does not exist` + 'Organization does not exist', ); } await hasPermission('retention_policy', `remove:${type}:organization`); - scopeId = organization.id - event = `${prefix}RetentionPolicyOrganization` + scopeId = organization.id; + event = `${prefix}RetentionPolicyOrganization`; break; - case "project": - const project = await projectHelpers(sqlClientPool).getProjectByProjectInput({name: input.scopeName}) + case 'project': + const project = await projectHelpers(sqlClientPool).getProjectByProjectInput({ name: input.scopeName }); if (!project) { throw new Error( - `Project does not exist` + 'Project does not exist', ); } await hasPermission('retention_policy', `remove:${type}:project`); - scopeId = project.id - event = `${prefix}RetentionPolicyProject` + scopeId = project.id; + event = `${prefix}RetentionPolicyProject`; break; default: throw new Error( - `No matching scope` + 'No matching scope', ); } const retpol = await Helpers(sqlClientPool).getRetentionPolicyByNameAndType(input.name, type); if (!retpol) { throw new Error( - `Retention policy does not exist` + 'Retention policy does not exist', ); } const retpoltypes = await Helpers(sqlClientPool).getRetentionPoliciesByTypePolicyIDAndLink(retpol.type, retpol.id, scopeId, input.scope); - if (retpoltypes.length == 0) { + if (retpoltypes.length === 0) { throw new Error( - `No matching retention policy attached to this ${input.scope}` + `No matching retention policy attached to this ${input.scope}`, ); } - let preDeleteProjectIds = [] - if (input.scope == "global") { + let preDeleteProjectIds = []; + if (input.scope === 'global') { // this is calculated before the policies are removed, as it is used after removing the policy being // passed into the post removal update hook if required, only for global scoped policies that are being unlinked - preDeleteProjectIds = await Helpers(sqlClientPool).getProjectIdsForAssociatedPolicyID(retpol.type, retpol.id, true) + preDeleteProjectIds = await Helpers(sqlClientPool).getProjectIdsForAssociatedPolicyID(retpol.type, retpol.id, true); } await query( @@ -413,66 +397,57 @@ const removeRetentionPolicyLink = async (sqlClientPool, hasPermission, userActiv retpol.id, input.scope, scopeId, - ) + ), ); // if a policy is unlinked to a scope (global, organization, project) // then run postRetentionPolicyLinkHook or postRetentionPolicyUpdateHook to make sure that the policy enforcer does // any policy updates for any impacted projects - if (input.scope != "global") { + if (input.scope !== 'global') { // if this is a standard organization or project policy unlink, then handle that with the post retention policy link hook // this hook knows how to check the change that impacts those two scopes - await Helpers(sqlClientPool).postRetentionPolicyLinkHook(scopeId, input.scope, retpol.type, retpol.id, true) + await Helpers(sqlClientPool).postRetentionPolicyLinkHook(scopeId, input.scope, retpol.type, retpol.id, true); } else { // global policy applications when they're remove require a different calculation step that will update // projects that don't use any policy overrides, this is because the depth of reach of a global policy // is a bit trickier to calculate - await Helpers(sqlClientPool).postRetentionPolicyUpdateHook(retpol.type, retpol.id, preDeleteProjectIds, true) + await Helpers(sqlClientPool).postRetentionPolicyUpdateHook(retpol.type, retpol.id, preDeleteProjectIds, true); } userActivityLogger(`User removed a ${type} retention policy '${retpol.name}' from organization`, { project: '', - event: event, + event, payload: { input: { retentionPolicy: retpol.id, scope: input.scope, - scopeId: scopeId - } - } + scopeId, + }, + }, }); - return "success" -} + return 'success'; +}; export const removeHarborRetentionPolicyLink: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await removeRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor') -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await removeRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'harbor'); export const removeHistoryRetentionPolicyLink: ResolverFn = async ( _root, { input }, - { sqlClientPool, hasPermission, userActivityLogger } -) => { - return await removeRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'history') -}; + { sqlClientPool, hasPermission, userActivityLogger }, +) => await removeRetentionPolicyLink(sqlClientPool, hasPermission, userActivityLogger, input, 'history'); export const getRetentionPoliciesByProjectId: ResolverFn = async ( project, args, - { sqlClientPool } + { sqlClientPool }, ) => { - - let pid = args.project; - if (project) { - pid = project.id; - } - let rows = [] - rows = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink(args.type, "project", project.id); + let rows = []; + rows = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink(args.type, 'project', project.id); return rows; }; @@ -480,14 +455,13 @@ export const getRetentionPoliciesByProjectId: ResolverFn = async ( export const getRetentionPoliciesByOrganizationId: ResolverFn = async ( organization, args, - { sqlClientPool } + { sqlClientPool }, ) => { - let oid = args.organization; if (organization) { oid = organization.id; } - let rows = [] - rows = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink(args.type, "organization", oid); + let rows = []; + rows = await Helpers(sqlClientPool).getRetentionPoliciesByScopeWithTypeAndLink(args.type, 'organization', oid); return rows; }; diff --git a/services/api/src/resources/retentionpolicy/sql.ts b/services/api/src/resources/retentionpolicy/sql.ts index 84cd64613c..20d5fdc375 100644 --- a/services/api/src/resources/retentionpolicy/sql.ts +++ b/services/api/src/resources/retentionpolicy/sql.ts @@ -11,118 +11,102 @@ export const Sql = { .update(updatePatch) .toString(); }, - selectRetentionPolicyById: (id: number) => - knex('retention_policy') - .where('id', '=', id) - .toString(), - selectRetentionPolicyByName: (name: string) => - knex('retention_policy') - .where('name', '=', name) - .toString(), - selectRetentionPolicyByNameAndType: (name: string, type: string) => - knex('retention_policy') - .where('name', '=', name) - .where('type', '=', type) - .toString(), - selectRetentionPoliciesByType: (type: string) => - knex('retention_policy') - .where('type', '=', type) - .toString(), - selectRetentionPoliciesByLink: (id: number, scope: string) => - knex('retention_policy as rp') - .select('rp.*') - .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') - .where(knex.raw('retention_policy_reference.scope = ?', scope)) - .andWhere(knex.raw('retention_policy_reference.id = ?', id)) - .toString(), - selectRetentionPoliciesByTypeAndLink: (type: string, id: number, scope: string) => - knex('retention_policy as rp') - .select('rp.*') - .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') - .where(knex.raw('retention_policy_reference.scope = ?', scope)) - .andWhere(knex.raw('retention_policy_reference.id = ?', id)) - .andWhere(knex.raw('rp.type = ?', type)) - .toString(), - selectRetentionPoliciesByTypePolicyIDAndLink: (type: string, policyId: number, id: number, scope: string) => - knex('retention_policy as rp') - .select('rp.*') - .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') - .where(knex.raw('retention_policy_reference.scope = ?', scope)) - .andWhere(knex.raw('retention_policy_reference.id = ?', id)) - .andWhere(knex.raw('rp.type = ?', type)) - .andWhere(knex.raw('rp.id = ?', policyId)) - .toString(), - selectRetentionPoliciesByLinkAndPolicyID: (id: number, scope: string) => - knex('retention_policy as rp') - .select('rp.*') - .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') - .where(knex.raw('retention_policy_reference.scope = ?', scope)) - .andWhere(knex.raw('rp.id = ?', id)) - .toString(), - selectScopeIDsByRetentionPolicyTypeExcludingPolicyID: (type: string, scope: string, policyId: number) => - knex('retention_policy as rp') - .select(knex.raw('group_concat(rpr.id) as scope_ids')) - .join('retention_policy_reference as rpr', 'rp.id', '=', 'rpr.retention_policy') - .where(knex.raw('rpr.scope = ?', scope)) - .andWhere(knex.raw('rp.type = ?', type)) - .whereNot(knex.raw('rp.id = ?', policyId)) - .toString(), - selectScopeIDsByRetentionPolicyTypeIncludingPolicyID: (type: string, scope: string, policyId: number) => - knex('retention_policy as rp') - .select(knex.raw('group_concat(rpr.id) as scope_ids')) - .join('retention_policy_reference as rpr', 'rp.id', '=', 'rpr.retention_policy') - .where(knex.raw('rpr.scope = ?', scope)) - .andWhere(knex.raw('rp.type = ?', type)) - .andWhere(knex.raw('rp.id = ?', policyId)) - .toString(), - deleteRetentionPolicy: (id: number) => - knex('retention_policy') - .where('id', '=', id) - .delete() - .toString(), - deleteRetentionPolicyLink: (id: number, scope: string, sid: number) => - knex('retention_policy_reference') - .where('retention_policy', '=', id) - .andWhere('scope', '=', scope) - .andWhere('id', '=', sid) - .delete() - .toString(), + selectRetentionPolicyById: (id: number) => knex('retention_policy') + .where('id', '=', id) + .toString(), + selectRetentionPolicyByName: (name: string) => knex('retention_policy') + .where('name', '=', name) + .toString(), + selectRetentionPolicyByNameAndType: (name: string, type: string) => knex('retention_policy') + .where('name', '=', name) + .where('type', '=', type) + .toString(), + selectRetentionPoliciesByType: (type: string) => knex('retention_policy') + .where('type', '=', type) + .toString(), + selectRetentionPoliciesByLink: (id: number, scope: string) => knex('retention_policy as rp') + .select('rp.*') + .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') + .where(knex.raw('retention_policy_reference.scope = ?', scope)) + .andWhere(knex.raw('retention_policy_reference.id = ?', id)) + .toString(), + selectRetentionPoliciesByTypeAndLink: (type: string, id: number, scope: string) => knex('retention_policy as rp') + .select('rp.*') + .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') + .where(knex.raw('retention_policy_reference.scope = ?', scope)) + .andWhere(knex.raw('retention_policy_reference.id = ?', id)) + .andWhere(knex.raw('rp.type = ?', type)) + .toString(), + selectRetentionPoliciesByTypePolicyIDAndLink: (type: string, policyId: number, id: number, scope: string) => knex('retention_policy as rp') + .select('rp.*') + .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') + .where(knex.raw('retention_policy_reference.scope = ?', scope)) + .andWhere(knex.raw('retention_policy_reference.id = ?', id)) + .andWhere(knex.raw('rp.type = ?', type)) + .andWhere(knex.raw('rp.id = ?', policyId)) + .toString(), + selectRetentionPoliciesByLinkAndPolicyID: (id: number, scope: string) => knex('retention_policy as rp') + .select('rp.*') + .join('retention_policy_reference', 'rp.id', '=', 'retention_policy_reference.retention_policy') + .where(knex.raw('retention_policy_reference.scope = ?', scope)) + .andWhere(knex.raw('rp.id = ?', id)) + .toString(), + selectScopeIDsByRetentionPolicyTypeExcludingPolicyID: (type: string, scope: string, policyId: number) => knex('retention_policy as rp') + .select(knex.raw('group_concat(rpr.id) as scope_ids')) + .join('retention_policy_reference as rpr', 'rp.id', '=', 'rpr.retention_policy') + .where(knex.raw('rpr.scope = ?', scope)) + .andWhere(knex.raw('rp.type = ?', type)) + .whereNot(knex.raw('rp.id = ?', policyId)) + .toString(), + selectScopeIDsByRetentionPolicyTypeIncludingPolicyID: (type: string, scope: string, policyId: number) => knex('retention_policy as rp') + .select(knex.raw('group_concat(rpr.id) as scope_ids')) + .join('retention_policy_reference as rpr', 'rp.id', '=', 'rpr.retention_policy') + .where(knex.raw('rpr.scope = ?', scope)) + .andWhere(knex.raw('rp.type = ?', type)) + .andWhere(knex.raw('rp.id = ?', policyId)) + .toString(), + deleteRetentionPolicy: (id: number) => knex('retention_policy') + .where('id', '=', id) + .delete() + .toString(), + deleteRetentionPolicyLink: (id: number, scope: string, sid: number) => knex('retention_policy_reference') + .where('retention_policy', '=', id) + .andWhere('scope', '=', scope) + .andWhere('id', '=', sid) + .delete() + .toString(), createRetentionPolicy: (input) => { const { id, name, type, - configuration + configuration, } = input; return knex('retention_policy').insert({ id, name, type, - configuration - }).toString(); - }, - addRetentionPolicyLink: (id: number, scope: string, sid: number) => { - return knex('retention_policy_reference').insert({ - retentionPolicy: id, - scope, - id: sid + configuration, }).toString(); }, - selectDeployTargetsForRetentionByProject: (pid: number) => - knex('project as p') - .select('p.name', 'p.id as pid', 'p.organization', 'dt.id as dtid', 'dt.name as dtname') - .join('environment as e', 'p.id', '=', 'e.project') - .join('openshift as dt', 'dt.id', '=', 'e.openshift') - .where('e.deleted', '0000-00-00 00:00:00') - .andWhere(knex.raw('p.id = ?', pid)) - .groupBy('p.name', 'e.openshift') - .toString(), - selectEnvironmentsForRetentionByProject: (pid: number) => - knex('project as p') - .select('p.name', 'p.id as pid', 'e.name as ename', 'e.id as eid', 'p.organization', 'dt.id as dtid', 'dt.name as dtname') - .join('environment as e', 'p.id', '=', 'e.project') - .join('openshift as dt', 'dt.id', '=', 'e.openshift') - .where('e.deleted', '0000-00-00 00:00:00') - .andWhere(knex.raw('p.id = ?', pid)) - .toString(), -} \ No newline at end of file + addRetentionPolicyLink: (id: number, scope: string, sid: number) => knex('retention_policy_reference').insert({ + retentionPolicy: id, + scope, + id: sid, + }).toString(), + selectDeployTargetsForRetentionByProject: (pid: number) => knex('project as p') + .select('p.name', 'p.id as pid', 'p.organization', 'dt.id as dtid', 'dt.name as dtname') + .join('environment as e', 'p.id', '=', 'e.project') + .join('openshift as dt', 'dt.id', '=', 'e.openshift') + .where('e.deleted', '0000-00-00 00:00:00') + .andWhere(knex.raw('p.id = ?', pid)) + .groupBy('p.name', 'e.openshift') + .toString(), + selectEnvironmentsForRetentionByProject: (pid: number) => knex('project as p') + .select('p.name', 'p.id as pid', 'e.name as ename', 'e.id as eid', 'p.organization', 'dt.id as dtid', 'dt.name as dtname') + .join('environment as e', 'p.id', '=', 'e.project') + .join('openshift as dt', 'dt.id', '=', 'e.openshift') + .where('e.deleted', '0000-00-00 00:00:00') + .andWhere(knex.raw('p.id = ?', pid)) + .toString(), +}; diff --git a/services/api/src/resources/retentionpolicy/types.ts b/services/api/src/resources/retentionpolicy/types.ts index 1f3f79d23c..85f6d18589 100644 --- a/services/api/src/resources/retentionpolicy/types.ts +++ b/services/api/src/resources/retentionpolicy/types.ts @@ -1,6 +1,6 @@ // the types for retention policies export interface HarborRetentionPolicy { - enabled: Boolean + enabled: boolean rules: [HarborRetentionRule] schedule: string } @@ -32,107 +32,107 @@ export type HarborRetentionMessage = { } export enum HarborRetentionMessageType { - HarborRetentionPolicy = "harborRetentionPolicy" + HarborRetentionPolicy = 'harborRetentionPolicy' } export enum HarborRetentionEventType { - RemovePolicy = "removePolicy", - UpdatePolicy = "updatePolicy" + RemovePolicy = 'removePolicy', + UpdatePolicy = 'updatePolicy' } export const RetentionPolicy = () => { - const convertHarborRetentionPolicyToJSON = async ( - harbor: HarborRetentionPolicy - ): Promise => { - const c = JSON.stringify(harbor) - return c - }; + const convertHarborRetentionPolicyToJSON = async ( + harbor: HarborRetentionPolicy, + ): Promise => { + const c = JSON.stringify(harbor); + return c; + }; - const convertHistoryRetentionPolicyToJSON = async ( - history: HistoryRetentionPolicy - ): Promise => { - const c = JSON.stringify(history) - return c - }; + const convertHistoryRetentionPolicyToJSON = async ( + history: HistoryRetentionPolicy, + ): Promise => { + const c = JSON.stringify(history); + return c; + }; - const convertJSONToHarborRetentionPolicy = async ( - configuration: string - ): Promise => { - const c = JSON.parse(configuration) - if (typeof c.enabled != "boolean") { - throw new Error("enabled must be a boolean"); - } - for (const rule of c.rules) { - if (typeof rule.name != "string") { - throw new Error(`${rule.name}: name must be a string`); - } - if (typeof rule.pattern != "string") { - throw new Error(`${rule.name}: pattern must be a string`); - } - if (typeof rule.latestPulled != "number") { - throw new Error(`${rule.name}: latestPulled must be a number`); - } - } - if (typeof c.schedule != "string") { - throw new Error("schedule must be a string"); - } - return c - }; + const convertJSONToHarborRetentionPolicy = async ( + configuration: string, + ): Promise => { + const c = JSON.parse(configuration); + if (typeof c.enabled !== 'boolean') { + throw new Error('enabled must be a boolean'); + } + for (const rule of c.rules) { + if (typeof rule.name !== 'string') { + throw new Error(`${rule.name}: name must be a string`); + } + if (typeof rule.pattern !== 'string') { + throw new Error(`${rule.name}: pattern must be a string`); + } + if (typeof rule.latestPulled !== 'number') { + throw new Error(`${rule.name}: latestPulled must be a number`); + } + } + if (typeof c.schedule !== 'string') { + throw new Error('schedule must be a string'); + } + return c; + }; - const convertJSONToHistoryRetentionPolicy = async ( - configuration: string - ): Promise => { - const c = JSON.parse(configuration) - if (typeof c.enabled != "boolean") { - throw new Error("enabled must be a boolean"); - } - if (typeof c.deploymentHistory != "number") { - throw new Error("deploymentHistory must be a number"); - } - if (typeof c.deploymentType != "string") { - throw new Error("deploymentHistory must be HistoryRetentionType"); - } - if (typeof c.taskHistory != "number") { - throw new Error("taskHistory must be a number"); - } - if (typeof c.taskType != "string") { - throw new Error("taskHistory must be HistoryRetentionType"); - } - return c - }; + const convertJSONToHistoryRetentionPolicy = async ( + configuration: string, + ): Promise => { + const c = JSON.parse(configuration); + if (typeof c.enabled !== 'boolean') { + throw new Error('enabled must be a boolean'); + } + if (typeof c.deploymentHistory !== 'number') { + throw new Error('deploymentHistory must be a number'); + } + if (typeof c.deploymentType !== 'string') { + throw new Error('deploymentHistory must be HistoryRetentionType'); + } + if (typeof c.taskHistory !== 'number') { + throw new Error('taskHistory must be a number'); + } + if (typeof c.taskType !== 'string') { + throw new Error('taskHistory must be HistoryRetentionType'); + } + return c; + }; - // run the configuration patches through the validation process - const returnValidatedHarborConfiguration = async (patch: any): Promise => { - const c = JSON.stringify(patch) - try { - await convertJSONToHarborRetentionPolicy(c) - return c - } catch (e) { - throw new Error( - `Provided harbor configuration is not valid: ${e}` - ); - } + // run the configuration patches through the validation process + const returnValidatedHarborConfiguration = async (patch: any): Promise => { + const c = JSON.stringify(patch); + try { + await convertJSONToHarborRetentionPolicy(c); + return c; + } catch (e) { + throw new Error( + `Provided harbor configuration is not valid: ${e}`, + ); } + }; - // run the configuration patches through the validation process - const returnValidatedHistoryConfiguration = async (patch: any): Promise => { - const c = JSON.stringify(patch) - try { - await convertJSONToHistoryRetentionPolicy(c) - return c - } catch (e) { - throw new Error( - `Provided history configuration is not valid: ${e}` - ); - } + // run the configuration patches through the validation process + const returnValidatedHistoryConfiguration = async (patch: any): Promise => { + const c = JSON.stringify(patch); + try { + await convertJSONToHistoryRetentionPolicy(c); + return c; + } catch (e) { + throw new Error( + `Provided history configuration is not valid: ${e}`, + ); } + }; - return { - convertHarborRetentionPolicyToJSON, - convertHistoryRetentionPolicyToJSON, - convertJSONToHarborRetentionPolicy, - convertJSONToHistoryRetentionPolicy, - returnValidatedHarborConfiguration, - returnValidatedHistoryConfiguration - }; -}; \ No newline at end of file + return { + convertHarborRetentionPolicyToJSON, + convertHistoryRetentionPolicyToJSON, + convertJSONToHarborRetentionPolicy, + convertJSONToHistoryRetentionPolicy, + returnValidatedHarborConfiguration, + returnValidatedHistoryConfiguration, + }; +}; diff --git a/services/api/src/resources/routes/helpers.ts b/services/api/src/resources/routes/helpers.ts index 78a07f27a2..2163ba90f4 100644 --- a/services/api/src/resources/routes/helpers.ts +++ b/services/api/src/resources/routes/helpers.ts @@ -2,7 +2,6 @@ import { Pool } from 'mariadb'; import { query } from '../../util/db'; import { Sql } from './sql'; import { isDNS1123Subdomain } from '../../util/func'; -import { logger } from '../../loggers/logger'; export const AnnotationLimit = 10; export const PathRoutesLimit = 10; @@ -13,62 +12,62 @@ export const Helpers = (sqlClientPool: Pool) => { const removeAllRoutesFromEnvironment = async (environmentId: number) => { await query( sqlClientPool, - Sql.removeAllRoutesFromEnvironment(environmentId) - ) - } + Sql.removeAllRoutesFromEnvironment(environmentId), + ); + }; const deleteAutogeneratedRoutesForEnvironment = async (environmentId: number) => { await query( sqlClientPool, - Sql.deleteAutogeneratedRoutesForEnvironment(environmentId) - ) - } + Sql.deleteAutogeneratedRoutesForEnvironment(environmentId), + ); + }; const deleteLagoonYAMLRoutesForEnvironment = async (environmentId: number) => { await query( sqlClientPool, - Sql.deleteLagoonYAMLRoutesForEnvironment(environmentId) - ) - } + Sql.deleteLagoonYAMLRoutesForEnvironment(environmentId), + ); + }; const deleteAutogeneratedRouteConfigForEnvironment = async (environmentId: number) => { await query( sqlClientPool, - Sql.deleteAutogeneratedRouteConfigForEnvironment(environmentId) - ) - } + Sql.deleteAutogeneratedRouteConfigForEnvironment(environmentId), + ); + }; const deleteAutogeneratedRouteConfigForProject = async (environmentId: number) => { await query( sqlClientPool, - Sql.deleteAutogeneratedRouteConfigForProject(environmentId) - ) - } + Sql.deleteAutogeneratedRouteConfigForProject(environmentId), + ); + }; const removeRouteFromEnvironment = async (domain: string, environmentId: number) => { const routes = await query( sqlClientPool, - Sql.selectRoutesByDomainAndEnvironmentID(domain, environmentId) - ) - if (routes.length == 0) { - throw new Error(`Route doesn't exist on this environment`); + Sql.selectRoutesByDomainAndEnvironmentID(domain, environmentId), + ); + if (routes.length === 0) { + throw new Error('Route doesn\'t exist on this environment'); } - const route = routes[0] + const route = routes[0]; await query( sqlClientPool, - Sql.removeRouteFromEnvironment(route.id) - ) - return route.id + Sql.removeRouteFromEnvironment(route.id), + ); + return route.id; }; const checkAnnotationRequirements = async (routeId: number, annotations: RouteAnnotations) => { - for(const annotation of annotations) { - const existingAnnotations = await query(sqlClientPool, Sql.selectRouteAnnotationsByRouteID(routeId)) + for (const annotation of annotations) { + const existingAnnotations = await query(sqlClientPool, Sql.selectRouteAnnotationsByRouteID(routeId)); const exists = existingAnnotations.some( - (a) => a.key === annotation.key && a.value === annotation.value + (a) => a.key === annotation.key && a.value === annotation.value, ); if (exists) { - throw new Error(`Annotation already exists on route`); + throw new Error('Annotation already exists on route'); } else { - const combinedAnnotationCount = existingAnnotations.length + 1 + const combinedAnnotationCount = existingAnnotations.length + 1; // arbitrary limit of 10 annotations, maybe this should be less? // would prefer that annotations done this way weren't a thing, but here we are if (combinedAnnotationCount >= AnnotationLimit) { - throw Error(`Limit of ${AnnotationLimit} annotations per route`) + throw Error(`Limit of ${AnnotationLimit} annotations per route`); } } } @@ -76,78 +75,78 @@ export const Helpers = (sqlClientPool: Pool) => { const checkDuplicateAnnotations = async (annotations: RouteAnnotations) => { const normalized = annotations.map(item => item.key.trim().toLowerCase()); if (new Set(normalized).size !== normalized.length) { - throw Error(`Duplicate annotation keys provided in annotations`) + throw Error('Duplicate annotation keys provided in annotations'); } if (annotations.length >= AnnotationLimit) { - throw Error(`Limit of ${AnnotationLimit} annotations per route`) + throw Error(`Limit of ${AnnotationLimit} annotations per route`); } }; const checkAlternativeNamesRequirements = async (alternativeNames: string[], projectId: number, routeId?: number) => { // check if the limit is not exceeded - let existingNames = 0 + let existingNames = 0; if (routeId) { const existingAltNames = await query( sqlClientPool, - Sql.selectRouteAlternativeDomainsByRouteID(routeId) - ) - existingNames = existingAltNames.length + Sql.selectRouteAlternativeDomainsByRouteID(routeId), + ); + existingNames = existingAltNames.length; } - const combinedAltDomainCount = existingNames + alternativeNames.length + const combinedAltDomainCount = existingNames + alternativeNames.length; if (combinedAltDomainCount >= AlternativeDomainsLimit) { - throw Error(`Limit of ${AlternativeDomainsLimit} alternative domains, consider removing some from this route, or create a new route`) + throw Error(`Limit of ${AlternativeDomainsLimit} alternative domains, consider removing some from this route, or create a new route`); } // if not exceeded, validate and check if not already exists for (const d of alternativeNames) { // check if the domain is valid dns subdomain if (!isDNS1123Subdomain(d)) { - throw Error(`'${d}' is not a valid domain`) + throw Error(`'${d}' is not a valid domain`); } const exists = await query( sqlClientPool, - Sql.selectRouteByDomainAndProjectID(d, projectId) - ) + Sql.selectRouteByDomainAndProjectID(d, projectId), + ); const exists2 = await query( sqlClientPool, - Sql.selectRouteAlternativeDomainsByDomainAndProjectID(d, projectId) - ) + Sql.selectRouteAlternativeDomainsByDomainAndProjectID(d, projectId), + ); // if the domains provided don't already exist, then add them if (exists.length > 0 || exists2.length > 0) { - throw Error(`Route already exists in this project`) + throw Error('Route already exists in this project'); } } - } + }; const checkDuplicateAlternativeNames = async (domainName: string, alternativeNames: string[]) => { for (const d of alternativeNames) { // trim spaces from the alternate domain as part of comparison check - const altDomain = d.trim() - if (altDomain == domainName) { - throw Error(`Main domain included in alternate domains`) + const altDomain = d.trim(); + if (altDomain === domainName) { + throw Error('Main domain included in alternate domains'); } } if (hasDuplicates(alternativeNames)) { - throw Error(`Duplicate domains provided in alternate domains`) + throw Error('Duplicate domains provided in alternate domains'); } }; const addRouteAnnotations = async (routeId: number, annotations: RouteAnnotations) => { - for(const annotation of annotations) { + for (const annotation of annotations) { await query( sqlClientPool, - Sql.insertRouteAnnotation({routeId, key: annotation.key, value: annotation.value}) - ) + Sql.insertRouteAnnotation({ routeId, key: annotation.key, value: annotation.value }), + ); } - } + }; const deleteRouteAnnotation = async (routeId: number, key: string) => { await query( sqlClientPool, - Sql.deleteRoutesAnnotation(routeId, key) - ) - } + Sql.deleteRoutesAnnotation(routeId, key), + ); + }; const deleteRouteAnnotations = async (routeId: number) => { await query( sqlClientPool, - Sql.deleteRoutesAnnotations(routeId) - ) - } + Sql.deleteRoutesAnnotations(routeId), + ); + }; return { removeAllRoutesFromEnvironment, @@ -163,16 +162,16 @@ export const Helpers = (sqlClientPool: Pool) => { deleteRouteAnnotations, deleteAutogeneratedRouteConfigForEnvironment, deleteAutogeneratedRouteConfigForProject, - } -} + }; +}; export function addServicePathRoute( pathRoutes: PathRoutes, newToService: string, - newPath: string + newPath: string, ): PathRoutes { const exists = pathRoutes.some( - (a) => a.toService === newToService && a.path === newPath + (a) => a.toService === newToService && a.path === newPath, ); if (!exists) { return [...pathRoutes, { toService: newToService, path: newPath }]; @@ -185,9 +184,9 @@ export function checkServicePathRouteRequirements( existingPathRoutes: PathRoutes, pathRoutes: PathRoutes, ) { - const combinedPathRoutesCount = pathRoutes.length + existingPathRoutes.length + const combinedPathRoutesCount = pathRoutes.length + existingPathRoutes.length; if (combinedPathRoutesCount > PathRoutesLimit) { - throw Error(`Limit of ${PathRoutesLimit} path routes, consider removing some from this route`) + throw Error(`Limit of ${PathRoutesLimit} path routes, consider removing some from this route`); } } @@ -195,7 +194,7 @@ export function checkServiceAutoGenPathRouteRequirements( pathRoutes: AutoGenPathRoutes, ) { if (pathRoutes.length > AutogeneratedPathRoutesLimit) { - throw Error(`Limit of ${AutogeneratedPathRoutesLimit} path routes for autogenerated routes`) + throw Error(`Limit of ${AutogeneratedPathRoutesLimit} path routes for autogenerated routes`); } } @@ -224,10 +223,10 @@ export type AutoGenPathRoutes = AutoGenPathRoute[]; export function removeServicePathRoute( pathRoutes: PathRoutes, targetToService: string, - targetPath: string + targetPath: string, ): PathRoutes { return pathRoutes.filter( - (a) => !(a.toService === targetToService && a.path === targetPath) + (a) => !(a.toService === targetToService && a.path === targetPath), ); } @@ -235,10 +234,10 @@ export function addAutogenServicePathRoute( pathRoutes: AutoGenPathRoutes, newFromService: string, newToService: string, - newPath: string + newPath: string, ): AutoGenPathRoutes { const exists = pathRoutes.some( - (a) => a.toService === newToService && a.path === newPath + (a) => a.toService === newToService && a.path === newPath, ); if (!exists) { @@ -252,10 +251,10 @@ export function removeAutogenServicePathRoute( pathRoutes: AutoGenPathRoutes, targetFromService: string, targetToService: string, - targetPath: string + targetPath: string, ): AutoGenPathRoutes { return pathRoutes.filter( - (a) => !(a.fromService === targetFromService &&a.toService === targetToService && a.path === targetPath) + (a) => !(a.fromService === targetFromService && a.toService === targetToService && a.path === targetPath), ); } @@ -263,4 +262,4 @@ export function removeAutogenServicePathRoute( function hasDuplicates(arr) { const normalized = arr.map(item => item.trim().toLowerCase()); return new Set(normalized).size !== normalized.length; -} \ No newline at end of file +} diff --git a/services/api/src/resources/routes/resolvers.ts b/services/api/src/resources/routes/resolvers.ts index fa97f9f292..ef146ad169 100644 --- a/services/api/src/resources/routes/resolvers.ts +++ b/services/api/src/resources/routes/resolvers.ts @@ -1,15 +1,17 @@ // @ts-ignore import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { knex, query } from '../../util/db'; import { Sql } from './sql'; import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; -import { addServicePathRoute, removeServicePathRoute, Helpers, PathRoutes, checkServicePathRouteRequirements, checkServiceAutoGenPathRouteRequirements } from './helpers'; +import { + addServicePathRoute, removeServicePathRoute, Helpers, PathRoutes, checkServicePathRouteRequirements, checkServiceAutoGenPathRouteRequirements, +} from './helpers'; import { AuditLog } from '../audit/types'; import { isDNS1123Subdomain } from '../../util/func'; -import { AuditType, RouteSource, RouteType } from '@lagoon/commons/dist/types'; +import { AuditType, RouteSource, RouteType } from '../../commons/types'; /* addRouteToProject is used to add a route to a project @@ -40,60 +42,62 @@ export const addRouteToProject: ResolverFn = async ( hstsMaxAge, monitoringPath, disableRequestVerification, - } + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } ) => { - const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project) - const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId) + const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project); + const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId); await hasPermission('route', 'add', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } // trim spaces from domain name - const domainName = domain.trim() + const domainName = domain.trim(); let environmentData; let environmentId = null; // let environmentServices; if (environment) { - const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - environmentData = env[0] - environmentId = environmentData.id + const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + environmentData = env[0]; + environmentId = environmentData.id; // environmentServices = await environmentHelpers(sqlClientPool).getEnvironmentServices(environmentId) } // check the route doesn't already exist in this project as either a top level route, or an alternative domain on another route const exists = await query( sqlClientPool, - Sql.selectRouteByDomainAndProjectID(domainName, projectId) - ) + Sql.selectRouteByDomainAndProjectID(domainName, projectId), + ); const exists2 = await query( sqlClientPool, - Sql.selectRouteAlternativeDomainsByDomainAndProjectID(domainName, projectId) - ) + Sql.selectRouteAlternativeDomainsByDomainAndProjectID(domainName, projectId), + ); // fail if the route already exists somewhere in the project if (exists.length > 0 || exists2.length > 0) { - throw Error(`Route already exists in this project`) + throw Error('Route already exists in this project'); } // check if the domain is valid dns subdomain if (!isDNS1123Subdomain(domainName)) { - throw Error(`'${domainName}' is not a valid domain`) + throw Error(`'${domainName}' is not a valid domain`); } if (environment && !service) { - throw Error(`Service is required when adding a domain linked to an environment`) + throw Error('Service is required when adding a domain linked to an environment'); } if (!environment && service) { - throw Error(`Environment is required when adding a domain linked to a service`) + throw Error('Environment is required when adding a domain linked to a service'); } // if (environment) { // // ensure the service exists on the environment @@ -109,7 +113,7 @@ export const addRouteToProject: ResolverFn = async ( if (alternativeNames !== undefined) { await Helpers(sqlClientPool).checkDuplicateAlternativeNames(domainName, alternativeNames); // check the route doesn't already exist in this project, and that any limits are not exceeded - await Helpers(sqlClientPool).checkAlternativeNamesRequirements(alternativeNames, projectId) + await Helpers(sqlClientPool).checkAlternativeNamesRequirements(alternativeNames, projectId); } // fail if duplicate annotations provided @@ -121,7 +125,7 @@ export const addRouteToProject: ResolverFn = async ( let pr: PathRoutes = []; if (environment) { if (pathRoutes !== undefined) { - checkServicePathRouteRequirements(pr, pathRoutes) + checkServicePathRouteRequirements(pr, pathRoutes); for (const pathRoute of pathRoutes) { // ensure the service exists on the environment // if (!environmentServices.some(item => item.name === pathRoute.toService)) { @@ -130,14 +134,14 @@ export const addRouteToProject: ResolverFn = async ( // // show a warning in the UI if the service doesn't exist // throw Error(`Service ${pathRoute.toService} in pathRoutes doesn't exist on this environment`) // } - pr = addServicePathRoute(pr, pathRoute.toService, pathRoute.path) + pr = addServicePathRoute(pr, pathRoute.toService, pathRoute.path); } } } // can only set type if assigning to an environment if (type && !environment) { - type = RouteType.STANDARD + type = RouteType.STANDARD; } else if (type && environment) { // can only add active/standby type if the environment supports it const isActive = type === RouteType.ACTIVE; @@ -145,8 +149,8 @@ export const addRouteToProject: ResolverFn = async ( if ((isActive || isStandby) && !projectData.standbyProductionEnvironment) { throw Error(`Can't add ${type} route to environment that isn't active or standby`); } - if ((isActive && projectData.standbyProductionEnvironment === environmentData.name) || - (isStandby && projectData.productionEnvironment === environmentData.name)) { + if ((isActive && projectData.standbyProductionEnvironment === environmentData.name) + || (isStandby && projectData.productionEnvironment === environmentData.name)) { throw Error(`Can't add ${type} route to ${isActive ? 'standby' : 'active'} environment`); } } @@ -154,14 +158,14 @@ export const addRouteToProject: ResolverFn = async ( if (!adminScopes.platformOwner) { // prevent users from creating routes a source that isn't api if (source && source !== RouteSource.API) { - throw Error(`Can only create routes with source API`); + throw Error('Can only create routes with source API'); } } /* WARNING: anything after this point makes changes to the route and how it is associated to a project or environment */ - if (primary == true && environment) { + if (primary === true && environment) { // check if another route isn't already the primary route, unset any other primary routes in this environment await query(sqlClientPool, Sql.unsetEnvironmentPrimaryRoute(environmentData.id, projectId)); } else { @@ -188,28 +192,28 @@ export const addRouteToProject: ResolverFn = async ( hstsIncludeSubdomains, hstsMaxAge, monitoringPath, - disableRequestVerification - }) + disableRequestVerification, + }), ); const rows = await query(sqlClientPool, Sql.selectRouteByID(insertId)); const route = R.prop(0, rows); // setup route annotations if provided if (annotations) { - await Helpers(sqlClientPool).addRouteAnnotations(route.id, annotations) + await Helpers(sqlClientPool).addRouteAnnotations(route.id, annotations); } // add the alternate domains if (alternativeNames !== undefined) { for (const d of alternativeNames) { // trim spaces from domain name - const altDomain = d.trim() + const altDomain = d.trim(); await query( sqlClientPool, Sql.insertRouteAlternativeDomain({ rid: insertId, domain: altDomain, - }) + }), ); } } @@ -225,8 +229,8 @@ export const addRouteToProject: ResolverFn = async ( auditLog.linkedResource = { id: environmentData.id.toString(), type: AuditType.ENVIRONMENT, - details: environmentData.name - } + details: environmentData.name, + }; } if (projectData.organization) { auditLog.organizationId = projectData.organization; @@ -237,8 +241,8 @@ export const addRouteToProject: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); return route; @@ -254,53 +258,55 @@ export const updateRouteOnProject: ResolverFn = async ( input: { domain, project, - patch - } + patch, + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } ) => { - const projectData = await projectHelpers(sqlClientPool).getProjectByName(project) - const projectId = projectData.id + const projectData = await projectHelpers(sqlClientPool).getProjectByName(project); + const projectId = projectData.id; await hasPermission('route', 'update', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } const existsProject = await query( sqlClientPool, - Sql.selectRouteByDomainAndProjectID(domain, projectId) - ) - if (existsProject.length == 0) { - throw Error(`Route doesn't exist on this project`) + Sql.selectRouteByDomainAndProjectID(domain, projectId), + ); + if (existsProject.length === 0) { + throw Error('Route doesn\'t exist on this project'); } - const route = existsProject[0] + const route = existsProject[0]; if (!adminScopes.platformOwner) { // prevent changing route type by general users except for yaml>api ownership switch (route.source) { case RouteSource.YAML: - if (patch.source && patch.source == RouteSource.API) { + if (patch.source && patch.source === RouteSource.API) { // if the route is being updated to be sourced from the API // allow it break; } // otherwise reject the update - throw Error(`Cannot update route managed by lagoon.yml`) + throw Error('Cannot update route managed by lagoon.yml'); case RouteSource.AUTOGENERATED: // reject update from general users - throw Error(`Cannot update autogenerated routes`) + throw Error('Cannot update autogenerated routes'); default: break; } } // set the updated timestamp on the patch - patch.updated = knex.fn.now() + patch.updated = knex.fn.now(); /* WARNING: anything after this point makes changes to the route @@ -309,8 +315,8 @@ export const updateRouteOnProject: ResolverFn = async ( sqlClientPool, Sql.updateRoute({ id: route.id, - patch: patch, - }) + patch, + }), ); const auditLog: AuditLog = { resource: { @@ -328,13 +334,13 @@ export const updateRouteOnProject: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); const ret = await query(sqlClientPool, Sql.selectRouteByID(route.id)); return ret[0]; -} +}; /* addOrUpdateRouteOnEnvironment is used to attach a route to an environment @@ -355,35 +361,35 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( service, primary, type, - } + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - const projectData = await projectHelpers(sqlClientPool).getProjectByName(project) - const projectId = projectData.id + const projectData = await projectHelpers(sqlClientPool).getProjectByName(project); + const projectId = projectData.id; await hasPermission('route', 'add:environment', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } - const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - const environmentData = env[0] + const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + const environmentData = env[0]; const existsProject = await query( sqlClientPool, - Sql.selectRouteByDomainAndProjectID(domain, projectId) - ) - if (existsProject.length == 0) { - throw Error(`Route doesn't exist on this project`) + Sql.selectRouteByDomainAndProjectID(domain, projectId), + ); + if (existsProject.length === 0) { + throw Error('Route doesn\'t exist on this project'); } - const route = existsProject[0] + const route = existsProject[0]; - if (primary == true) { + if (primary === true) { // check if another route isn't already the primary route, unset any other primary routes in this environment await query(sqlClientPool, Sql.unsetEnvironmentPrimaryRoute(environmentData.id, route.project)); } @@ -395,8 +401,8 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( if ((isActive || isStandby) && !projectData.standbyProductionEnvironment) { throw Error(`Can't add ${type} route to environment that isn't active or standby`); } - if ((isActive && projectData.standbyProductionEnvironment === environmentData.name) || - (isStandby && projectData.productionEnvironment === environmentData.name)) { + if ((isActive && projectData.standbyProductionEnvironment === environmentData.name) + || (isStandby && projectData.productionEnvironment === environmentData.name)) { throw Error(`Can't add ${type} route to ${isActive ? 'standby' : 'active'} environment`); } } @@ -408,7 +414,7 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( // do nothing break; default: - throw Error(`Route is already attached to another environment`) + throw Error('Route is already attached to another environment'); } // ensure the service exists on the environment @@ -424,7 +430,7 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( let pr: PathRoutes = []; if (environment) { if (pathRoutes !== undefined) { - checkServicePathRouteRequirements(JSON.parse(route.pathRoutes), pathRoutes) + checkServicePathRouteRequirements(JSON.parse(route.pathRoutes), pathRoutes); for (const pathRoute of pathRoutes) { // ensure the service exists on the environment // if (!environmentServices.some(item => item.name === pathRoute.toService)) { @@ -433,7 +439,7 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( // // show a warning in the UI if the service doesn't exist // throw Error(`Service ${pathRoute.toService} in pathRoutes doesn't exist on this environment`) // } - pr = addServicePathRoute(pr, pathRoute.toService, pathRoute.path) + pr = addServicePathRoute(pr, pathRoute.toService, pathRoute.path); } } } @@ -452,8 +458,8 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( updated: knex.fn.now(), primary, type, - } - }) + }, + }), ); const auditLog: AuditLog = { @@ -467,8 +473,8 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( auditLog.linkedResource = { id: environmentData.id.toString(), type: AuditType.ENVIRONMENT, - details: environmentData.name - } + details: environmentData.name, + }; } if (projectData.organization) { auditLog.organizationId = projectData.organization; @@ -480,13 +486,13 @@ export const addOrUpdateRouteOnEnvironment: ResolverFn = async ( project: projectData.id, environment: environmentData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); const ret = await query(sqlClientPool, Sql.selectRouteByID(route.id)); return ret[0]; -} +}; /* activeStandbyRouteMove is used by the activestandby task completion @@ -503,21 +509,23 @@ export const activeStandbyRouteMove: ResolverFn = async ( type, }, }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes} + { + sqlClientPool, userActivityLogger, adminScopes, + }, ) => { if (adminScopes.platformOwner) { - const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project) - const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - const environmentData = env[0] + const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project); + const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + const environmentData = env[0]; const existsProject = await query( sqlClientPool, - Sql.selectRouteByDomainAndProjectID(domain, projectId) - ) - if (existsProject.length == 0) { - throw Error(`Route doesn't exist on this project`) + Sql.selectRouteByDomainAndProjectID(domain, projectId), + ); + if (existsProject.length === 0) { + throw Error('Route doesn\'t exist on this project'); } - const route = existsProject[0] + const route = existsProject[0]; // ensure the service exists on the environment // const environmentServices = await environmentHelpers(sqlClientPool).getEnvironmentServices(environmentData.id) @@ -536,8 +544,8 @@ export const activeStandbyRouteMove: ResolverFn = async ( environment: environmentData.id, service, type, - } - }) + }, + }), ); userActivityLogger(`User moved route '${route.domain}' to environment '${environmentData.name}'`, { @@ -546,19 +554,18 @@ export const activeStandbyRouteMove: ResolverFn = async ( payload: { project: projectId, environment: environmentData.id, - route: route.id - } + route: route.id, + }, }); const ret = await query(sqlClientPool, Sql.selectRouteByID(route.id)); return ret[0]; - } else { - // throw unauthorized error - throw new Error( - `Unauthorized` - ); } -} + // throw unauthorized error + throw new Error( + 'Unauthorized', + ); +}; /* removeRouteFromEnvironment is used to remove a route from an environment @@ -576,47 +583,49 @@ export const removeRouteFromEnvironment: ResolverFn = async ( domain, project, environment, - } + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } ) => { - const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project) - const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId) + const projectId = await projectHelpers(sqlClientPool).getProjectIdByName(project); + const projectData = await projectHelpers(sqlClientPool).getProjectById(projectId); await hasPermission('route', 'remove:environment', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } - const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - const environmentData = env[0] + const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + const environmentData = env[0]; const existsProject = await query( sqlClientPool, - Sql.selectRouteByDomainAndProjectID(domain, projectId) - ) - if (existsProject.length == 0) { - throw Error(`Route doesn't exist on this project`) + Sql.selectRouteByDomainAndProjectID(domain, projectId), + ); + if (existsProject.length === 0) { + throw Error('Route doesn\'t exist on this project'); } - const route = existsProject[0] + const route = existsProject[0]; if (!adminScopes.platformOwner) { if (route.source.toLowerCase() === RouteSource.YAML) { - throw Error(`This route cannot be removed from the environment as it is managed by a lagoon.yml file`) + throw Error('This route cannot be removed from the environment as it is managed by a lagoon.yml file'); } if (route.source.toLowerCase() === RouteSource.AUTOGENERATED) { - throw Error(`Cannot remove autogenerated routes from an environment using this endpoint`) + throw Error('Cannot remove autogenerated routes from an environment using this endpoint'); } } /* WARNING: anything after this point makes changes to the route */ - const routeId = await Helpers(sqlClientPool).removeRouteFromEnvironment(domain, environmentData.id) + const routeId = await Helpers(sqlClientPool).removeRouteFromEnvironment(domain, environmentData.id); const ret = await query(sqlClientPool, Sql.selectRouteByID(routeId)); const returnRoute = ret[0]; @@ -632,8 +641,8 @@ export const removeRouteFromEnvironment: ResolverFn = async ( auditLog.linkedResource = { id: environmentData.id.toString(), type: AuditType.ENVIRONMENT, - details: environmentData.name - } + details: environmentData.name, + }; } if (projectData.organization) { auditLog.organizationId = projectData.organization; @@ -645,12 +654,12 @@ export const removeRouteFromEnvironment: ResolverFn = async ( project: projectData.id, environment: environmentData.id, route: returnRoute.id, - ...auditLog - } + ...auditLog, + }, }); return returnRoute; -} +}; /* addRouteAlternativeDomains can be used to extend an existing route with any subject alternative domains @@ -664,30 +673,30 @@ export const addRouteAlternativeDomains: ResolverFn = async ( input: { id, alternativeNames, - } + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rows = await query(sqlClientPool, Sql.selectRouteByID(id)); - if (rows.length == 0) { - throw new Error(`Unauthorized: You don't have permission to "add" on "route"`); + if (rows.length === 0) { + throw new Error('Unauthorized: You don\'t have permission to "add" on "route"'); } const route = R.prop(0, rows); - const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project) + const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project); await hasPermission('route', 'add', { - project: projectData.id + project: projectData.id, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } if (alternativeNames !== undefined) { - await Helpers(sqlClientPool).checkDuplicateAlternativeNames(route.domain, alternativeNames) + await Helpers(sqlClientPool).checkDuplicateAlternativeNames(route.domain, alternativeNames); // check the route doesn't already exist in this project, and that any limits are not exceeded - await Helpers(sqlClientPool).checkAlternativeNamesRequirements(alternativeNames, route.project) + await Helpers(sqlClientPool).checkAlternativeNamesRequirements(alternativeNames, route.project); } /* @@ -701,7 +710,7 @@ export const addRouteAlternativeDomains: ResolverFn = async ( Sql.insertRouteAlternativeDomain({ rid: id, domain: d, - }) + }), ); } } @@ -722,12 +731,12 @@ export const addRouteAlternativeDomains: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); return route; -} +}; /* removeRouteAlternativeDomain will remove an alternative domain from an existing route @@ -738,30 +747,30 @@ export const removeRouteAlternativeDomain: ResolverFn = async ( input: { id, domain, - } + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rows = await query(sqlClientPool, Sql.selectRouteByID(id)); - if (rows.length == 0) { - throw new Error(`Unauthorized: You don't have permission to "add" on "route"`); + if (rows.length === 0) { + throw new Error('Unauthorized: You don\'t have permission to "add" on "route"'); } const route = R.prop(0, rows); - const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project) + const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project); await hasPermission('route', 'add', { - project: projectData.id + project: projectData.id, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } const alternateDomain = await query( sqlClientPool, - Sql.selectRouteAlternativeDomainsByDomainAndProjectID(domain, route.project) - ) + Sql.selectRouteAlternativeDomainsByDomainAndProjectID(domain, route.project), + ); /* WARNING: anything after this point makes changes to the route @@ -770,7 +779,7 @@ export const removeRouteAlternativeDomain: ResolverFn = async ( const ad = R.prop(0, alternateDomain); await query(sqlClientPool, Sql.deleteRouteAlternativeDomain(ad.id)); } else { - throw Error(`Domain doesn't exist on this route`) + throw Error('Domain doesn\'t exist on this route'); } const auditLog: AuditLog = { @@ -789,12 +798,12 @@ export const removeRouteAlternativeDomain: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); return route; -} +}; /* addRouteAnnotation allows for annotations to be added to a route @@ -809,24 +818,24 @@ export const addRouteAnnotation: ResolverFn = async ( input: { id, annotations, - } + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rows = await query(sqlClientPool, Sql.selectRouteByID(id)); - if (rows.length == 0) { - throw new Error(`Unauthorized: You don't have permission to "add" on "route"`); + if (rows.length === 0) { + throw new Error('Unauthorized: You don\'t have permission to "add" on "route"'); } const route = R.prop(0, rows); - const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project) + const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project); await hasPermission('route', 'add', { - project: projectData.id + project: projectData.id, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } // fail if duplicate annotations provided @@ -837,16 +846,16 @@ export const addRouteAnnotation: ResolverFn = async ( /* WARNING: anything after this point makes changes to the route */ - await Helpers(sqlClientPool).addRouteAnnotations(route.id, annotations) + await Helpers(sqlClientPool).addRouteAnnotations(route.id, annotations); await query( sqlClientPool, Sql.updateRoute({ - id: id, + id, patch: { updated: knex.fn.now(), - } - }) + }, + }), ); const ret = await query(sqlClientPool, Sql.selectRouteByID(id)); @@ -868,12 +877,12 @@ export const addRouteAnnotation: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); return retRoute; -} +}; /* removeRouteAnnotation will remove an annotation from a route @@ -884,39 +893,39 @@ export const removeRouteAnnotation: ResolverFn = async ( input: { id, key, - } + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rows = await query(sqlClientPool, Sql.selectRouteByID(id)); - if (rows.length == 0) { - throw new Error(`Unauthorized: You don't have permission to "add" on "route"`); + if (rows.length === 0) { + throw new Error('Unauthorized: You don\'t have permission to "add" on "route"'); } const route = R.prop(0, rows); - const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project) + const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project); await hasPermission('route', 'add', { - project: projectData.id + project: projectData.id, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } /* WARNING: anything after this point makes changes to the route */ - await Helpers(sqlClientPool).deleteRouteAnnotation(route.id, key) + await Helpers(sqlClientPool).deleteRouteAnnotation(route.id, key); await query( sqlClientPool, Sql.updateRoute({ - id: id, + id, patch: { updated: knex.fn.now(), - } - }) + }, + }), ); const ret = await query(sqlClientPool, Sql.selectRouteByID(id)); @@ -938,12 +947,12 @@ export const removeRouteAnnotation: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); return retRoute; -} +}; /* addPathRoutesToRoute is a way to extend a route with the `pathRoutes` feature @@ -955,42 +964,42 @@ export const addPathRoutesToRoute: ResolverFn = async ( input: { id, pathRoutes, - } + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rows = await query(sqlClientPool, Sql.selectRouteByID(id)); - if (rows.length == 0) { - throw new Error(`Unauthorized: You don't have permission to "add" on "route"`); + if (rows.length === 0) { + throw new Error('Unauthorized: You don\'t have permission to "add" on "route"'); } const route = rows[0]; - const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project) + const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project); await hasPermission('route', 'add', { - project: projectData.id + project: projectData.id, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } let pr: PathRoutes = []; - pr = JSON.parse(route.pathRoutes) + pr = JSON.parse(route.pathRoutes); if (pathRoutes !== undefined) { - checkServicePathRouteRequirements(pr, pathRoutes) + checkServicePathRouteRequirements(pr, pathRoutes); for (const pathRoute of pathRoutes) { - pr = addServicePathRoute(pr, pathRoute.toService, pathRoute.path) + pr = addServicePathRoute(pr, pathRoute.toService, pathRoute.path); } } - route.pathRoutes = JSON.stringify(pr) + route.pathRoutes = JSON.stringify(pr); - let patch = { + const patch = { pathRoutes: JSON.stringify(pr), updated: knex.fn.now(), - } + }; /* WARNING: anything after this point makes changes to the route @@ -998,9 +1007,9 @@ export const addPathRoutesToRoute: ResolverFn = async ( await query( sqlClientPool, Sql.updateRoute({ - id: id, - patch: patch - }) + id, + patch, + }), ); const auditLog: AuditLog = { @@ -1019,15 +1028,15 @@ export const addPathRoutesToRoute: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); const ret = await query(sqlClientPool, Sql.selectRouteByID(id)); const retRoute = ret[0]; return retRoute; -} +}; /* removePathRouteFromRoute will remove a path route @@ -1038,38 +1047,38 @@ export const removePathRouteFromRoute: ResolverFn = async ( input: { id, toService, - path - } + path, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { const rows = await query(sqlClientPool, Sql.selectRouteByID(id)); - if (rows.length == 0) { - throw new Error(`Unauthorized: You don't have permission to "add" on "route"`); + if (rows.length === 0) { + throw new Error('Unauthorized: You don\'t have permission to "add" on "route"'); } const route = R.prop(0, rows); - const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project) + const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project); await hasPermission('route', 'add', { - project: projectData.id + project: projectData.id, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } let pr: PathRoutes = []; - pr = JSON.parse(route.pathRoutes) + pr = JSON.parse(route.pathRoutes); - pr = removeServicePathRoute(pr, toService, path) - route.pathRoutes = JSON.stringify(pr) + pr = removeServicePathRoute(pr, toService, path); + route.pathRoutes = JSON.stringify(pr); - let patch = { + const patch = { pathRoutes: JSON.stringify(pr), updated: knex.fn.now(), - } + }; /* WARNING: anything after this point makes changes to the route @@ -1077,9 +1086,9 @@ export const removePathRouteFromRoute: ResolverFn = async ( await query( sqlClientPool, Sql.updateRoute({ - id: id, - patch: patch - }) + id, + patch, + }), ); const auditLog: AuditLog = { @@ -1098,15 +1107,15 @@ export const removePathRouteFromRoute: ResolverFn = async ( payload: { project: projectData.id, route: route.id, - ...auditLog - } + ...auditLog, + }, }); const ret = await query(sqlClientPool, Sql.selectRouteByID(id)); const retRoute = R.prop(0, ret); return retRoute; -} +}; /* deleteRoute does what it says on the tin, will delete a route from a project @@ -1114,31 +1123,33 @@ export const removePathRouteFromRoute: ResolverFn = async ( export const deleteRoute: ResolverFn = async ( root, { input: { id } }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { const rows = await query(sqlClientPool, Sql.selectRouteByID(id)); - if (rows.length == 0) { - throw new Error(`Unauthorized: You don't have permission to "delete" on "route"`); + if (rows.length === 0) { + throw new Error('Unauthorized: You don\'t have permission to "delete" on "route"'); } - const route = rows[0] - const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project) + const route = rows[0]; + const projectData = await projectHelpers(sqlClientPool).getProjectById(route.project); await hasPermission('route', 'delete', { - project: projectData.id + project: projectData.id, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } if (!adminScopes.platformOwner) { // general users can't delete routes with these sources from the api - if (route.source.toLowerCase() == RouteSource.AUTOGENERATED) { - throw new Error(`Cannot delete autogenerated routes, you must modify the project or environment autogenerated route configuration`); + if (route.source.toLowerCase() === RouteSource.AUTOGENERATED) { + throw new Error('Cannot delete autogenerated routes, you must modify the project or environment autogenerated route configuration'); } - if (route.source.toLowerCase() == RouteSource.YAML) { - throw new Error(`Cannot delete routes that are managed by a lagoon.yml file, either modify the route in the api or delete it from the lagoon.yml file.`); + if (route.source.toLowerCase() === RouteSource.YAML) { + throw new Error('Cannot delete routes that are managed by a lagoon.yml file, either modify the route in the api or delete it from the lagoon.yml file.'); } } // @TODO: do we want to block deletion of routes if they are attached to an environment? @@ -1165,8 +1176,8 @@ export const deleteRoute: ResolverFn = async ( event: 'api:deleteRoute', payload: { route: route.id, - ...auditLog - } + ...auditLog, + }, }); return 'success'; @@ -1178,10 +1189,10 @@ export const deleteRoute: ResolverFn = async ( export const getRoutesByProjectId: ResolverFn = async ( { id: projectId }, { domain }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { await hasPermission('route', 'view', { - project: projectId + project: projectId, }); let queryBuilder = knex('routes') @@ -1201,11 +1212,11 @@ export const getRoutesByProjectId: ResolverFn = async ( export const getRoutesByEnvironmentId: ResolverFn = async ( { id: environmentId }, { domain, source }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const { projectId } = await projectHelpers(sqlClientPool).getProjectByEnvironmentId(environmentId); await hasPermission('route', 'view', { - project: projectId + project: projectId, }); let queryBuilder = knex('routes') @@ -1217,7 +1228,7 @@ export const getRoutesByEnvironmentId: ResolverFn = async ( } if (source) { - queryBuilder = queryBuilder.andWhere('source', source) + queryBuilder = queryBuilder.andWhere('source', source); } return query(sqlClientPool, queryBuilder.toString()); @@ -1230,11 +1241,11 @@ export const getRoutesByEnvironmentId: ResolverFn = async ( export const getAlternateRoutesByRouteId: ResolverFn = async ( { id: rid }, args, - { sqlClientPool, } + { sqlClientPool }, ) => { const rows = await query( sqlClientPool, - Sql.selectRouteAlternativeDomainsByRouteID(rid) + Sql.selectRouteAlternativeDomainsByRouteID(rid), ); return rows; @@ -1247,11 +1258,11 @@ export const getAlternateRoutesByRouteId: ResolverFn = async ( export const getRouteAnnotationsByRouteId: ResolverFn = async ( { id: rid }, args, - { sqlClientPool } + { sqlClientPool }, ) => { const rows = await query( sqlClientPool, - Sql.selectRouteAnnotationsByRouteID(rid) + Sql.selectRouteAnnotationsByRouteID(rid), ); return rows; @@ -1263,11 +1274,9 @@ export const getRouteAnnotationsByRouteId: ResolverFn = async ( */ export const getPathRoutesByRouteId: ResolverFn = async ( input, - args, - { } -) => { - return JSON.parse(input.pathRoutes); -}; + _args, + { }, +) => JSON.parse(input.pathRoutes); /* updateAutogeneratedRouteConfigOnProject is used to add or update the autogenerated route configuration for a project @@ -1277,48 +1286,48 @@ export const updateAutogeneratedRouteConfigOnProject: ResolverFn = async ( { input: { project, - patch - } + patch, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - const projectData = await projectHelpers(sqlClientPool).getProjectByName(project) - const projectId = projectData.id + const projectData = await projectHelpers(sqlClientPool).getProjectByName(project); + const projectId = projectData.id; await hasPermission('route', 'update', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } // set the updated timestamp on the patch - patch.updated = knex.fn.now() - patch.type = 'project' - patch.typeId = projectData.id + patch.updated = knex.fn.now(); + patch.type = 'project'; + patch.typeId = projectData.id; - let pathRoutes + let pathRoutes; if (patch.pathRoutes) { - checkServiceAutoGenPathRouteRequirements(patch.pathRoutes) - pathRoutes = JSON.stringify(patch.pathRoutes) + checkServiceAutoGenPathRouteRequirements(patch.pathRoutes); + pathRoutes = JSON.stringify(patch.pathRoutes); } if (patch.pathRoutes === null) { - pathRoutes = null + pathRoutes = null; } - patch.pathRoutes = pathRoutes + patch.pathRoutes = pathRoutes; const createOrUpdateSql = knex('routes_autogenerated_configuration') .insert({ - ...patch + ...patch, }) .onConflict('autogenerated_route_config_type') .merge({ - ...patch + ...patch, }) .toString(); - const { insertId } = await query(sqlClientPool, createOrUpdateSql); + const { _insertId } = await query(sqlClientPool, createOrUpdateSql); const auditLog: AuditLog = { resource: { @@ -1335,14 +1344,13 @@ export const updateAutogeneratedRouteConfigOnProject: ResolverFn = async ( event: 'api:updateAutogeneratedRouteConfigOnProject', payload: { project: projectData.id, - ...auditLog - } + ...auditLog, + }, }); const ret = await query(sqlClientPool, Sql.selectAutogeneratedRouteConfigByProjectID(projectData.id)); return ret[0]; -} - +}; /* removeAutogeneratedRouteConfigFromProject is used to remove the autogenerated route configuration for a project @@ -1351,30 +1359,30 @@ export const updateAutogeneratedRouteConfigOnProject: ResolverFn = async ( export const removeAutogeneratedRouteConfigFromProject: ResolverFn = async ( root, { - project + project, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - const projectData = await projectHelpers(sqlClientPool).getProjectByName(project) - const projectId = projectData.id + const projectData = await projectHelpers(sqlClientPool).getProjectByName(project); + const projectId = projectData.id; await hasPermission('route', 'update', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } - await query(sqlClientPool, Sql.deleteAutogeneratedRouteConfigForProject(projectData.id)) + await query(sqlClientPool, Sql.deleteAutogeneratedRouteConfigForProject(projectData.id)); const auditLog: AuditLog = { resource: { id: projectData.id.toString(), type: AuditType.PROJECT, details: projectData.name, - } + }, }; if (projectData.organization) { auditLog.organizationId = projectData.organization; @@ -1384,12 +1392,12 @@ export const removeAutogeneratedRouteConfigFromProject: ResolverFn = async ( event: 'api:removeAutogeneratedRouteConfigFromProject', payload: { project: projectData.id, - ...auditLog - } + ...auditLog, + }, }); - return 'success' -} + return 'success'; +}; /* updateAutogeneratedRouteConfigOnEnvironment is used to add or update the autogenerated route configuration for an environment @@ -1400,52 +1408,51 @@ export const updateAutogeneratedRouteConfigOnEnvironment: ResolverFn = async ( input: { project, environment, - patch - } + patch, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - const projectData = await projectHelpers(sqlClientPool).getProjectByName(project) - const projectId = projectData.id + const projectData = await projectHelpers(sqlClientPool).getProjectByName(project); + const projectId = projectData.id; await hasPermission('route', 'update', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } - let environmentData; - const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - environmentData = env[0] + const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + const environmentData = env[0]; // set the updated timestamp on the patch - patch.updated = knex.fn.now() - patch.type = 'environment' - patch.typeId = environmentData.id + patch.updated = knex.fn.now(); + patch.type = 'environment'; + patch.typeId = environmentData.id; - let pathRoutes + let pathRoutes; if (patch.pathRoutes) { - checkServiceAutoGenPathRouteRequirements(patch.pathRoutes) - pathRoutes = JSON.stringify(patch.pathRoutes) + checkServiceAutoGenPathRouteRequirements(patch.pathRoutes); + pathRoutes = JSON.stringify(patch.pathRoutes); } if (patch.pathRoutes === null) { - pathRoutes = null + pathRoutes = null; } - patch.pathRoutes = pathRoutes + patch.pathRoutes = pathRoutes; const createOrUpdateSql = knex('routes_autogenerated_configuration') .insert({ - ...patch + ...patch, }) .onConflict('autogenerated_route_config_type') .merge({ - ...patch + ...patch, }) .toString(); - const { insertId } = await query(sqlClientPool, createOrUpdateSql); + const { _insertId } = await query(sqlClientPool, createOrUpdateSql); const auditLog: AuditLog = { resource: { @@ -1456,8 +1463,8 @@ export const updateAutogeneratedRouteConfigOnEnvironment: ResolverFn = async ( linkedResource: { id: environmentData.id.toString(), type: AuditType.ENVIRONMENT, - details: environmentData.name - } + details: environmentData.name, + }, }; if (projectData.organization) { auditLog.organizationId = projectData.organization; @@ -1467,13 +1474,13 @@ export const updateAutogeneratedRouteConfigOnEnvironment: ResolverFn = async ( event: 'api:updateAutogeneratedRouteConfigOnEnvironment', payload: { project: projectData.id, - ...auditLog - } + ...auditLog, + }, }); const ret = await query(sqlClientPool, Sql.selectAutogeneratedRouteConfigByEnvironmentID(environmentData.id)); return ret[0]; -} +}; /* removeAutogeneratedRouteConfigFromEnvironment is used to remove the autogenerated route configuration for an environment @@ -1485,25 +1492,24 @@ export const removeAutogeneratedRouteConfigFromEnvironment: ResolverFn = async ( project, environment, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { - const projectData = await projectHelpers(sqlClientPool).getProjectByName(project) - const projectId = projectData.id + const projectData = await projectHelpers(sqlClientPool).getProjectByName(project); + const projectId = projectData.id; await hasPermission('route', 'update', { - project: projectId + project: projectId, }); // this is used to set the beta feature flag on a project from the organization // this feature will eventually be made generally available and the feature flag will be removed if (await projectHelpers(sqlClientPool).checkApiRoutesFeature(projectData.organization) === false) { - throw Error(`This feature is currently unavailable`) + throw Error('This feature is currently unavailable'); } - let environmentData; - const env = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId) - environmentData = env[0] + const env2 = await environmentHelpers(sqlClientPool).getEnvironmentByNameAndProject(environment, projectId); + const environmentData = env2[0]; - await query(sqlClientPool, Sql.deleteAutogeneratedRouteConfigForEnvironment(environmentData.id)) + await query(sqlClientPool, Sql.deleteAutogeneratedRouteConfigForEnvironment(environmentData.id)); const auditLog: AuditLog = { resource: { @@ -1514,8 +1520,8 @@ export const removeAutogeneratedRouteConfigFromEnvironment: ResolverFn = async ( linkedResource: { id: environmentData.id.toString(), type: AuditType.ENVIRONMENT, - details: environmentData.name - } + details: environmentData.name, + }, }; if (projectData.organization) { auditLog.organizationId = projectData.organization; @@ -1525,12 +1531,12 @@ export const removeAutogeneratedRouteConfigFromEnvironment: ResolverFn = async ( event: 'api:removeAutogeneratedRouteConfigFromEnvironment', payload: { project: projectData.id, - ...auditLog - } + ...auditLog, + }, }); - return 'success' -} + return 'success'; +}; /* getAutogeneratedRouteConfigByEnvironmentId is a field resolver @@ -1539,11 +1545,11 @@ export const removeAutogeneratedRouteConfigFromEnvironment: ResolverFn = async ( export const getAutogeneratedRouteConfigByEnvironmentId: ResolverFn = async ( { id }, args, - { sqlClientPool } + { sqlClientPool }, ) => { const rows = await query( sqlClientPool, - Sql.selectAutogeneratedRouteConfigByEnvironmentID(id) + Sql.selectAutogeneratedRouteConfigByEnvironmentID(id), ); if (rows.length) { return rows[0]; @@ -1558,11 +1564,11 @@ export const getAutogeneratedRouteConfigByEnvironmentId: ResolverFn = async ( export const getAutogeneratedRouteConfigByProjectId: ResolverFn = async ( { id }, args, - { sqlClientPool } + { sqlClientPool }, ) => { const rows = await query( sqlClientPool, - Sql.selectAutogeneratedRouteConfigByProjectID(id) + Sql.selectAutogeneratedRouteConfigByProjectID(id), ); if (rows.length) { return rows[0]; @@ -1576,8 +1582,8 @@ export const getAutogeneratedRouteConfigByProjectId: ResolverFn = async ( */ export const getAutogeneratedPathRoutes: ResolverFn = async ( input, - args, - { } + _args, + { }, ) => { if (!input?.pathRoutes) { return []; @@ -1585,38 +1591,35 @@ export const getAutogeneratedPathRoutes: ResolverFn = async ( return JSON.parse(input.pathRoutes); }; - /* getAutogeneratedRoutePrefixes is a field resolver it has no permission checks as it isn't called directly */ export const getAutogeneratedRoutePrefixes: ResolverFn = async ( input, - args, - { } + _args, + { }, ) => { if (!input?.prefixes) { return []; } - return input.prefixes?.split(","); + return input.prefixes?.split(','); }; - - - // // this replaces the prefixes in their entirety if provided - // let joinedPrefixes - // if (autogeneratedRoutePrefixes) { - // joinedPrefixes = autogeneratedRoutePrefixes.join(',') - // } - // if (autogeneratedRoutePrefixes === null) { - // joinedPrefixes = null - // } - - // // this replaces the pathroutes in their entirety if provided - // let pathRoutes - // if (autogeneratedPathRoutes) { - // pathRoutes = JSON.stringify(autogeneratedPathRoutes) - // } - // if (autogeneratedPathRoutes === null) { - // pathRoutes = null - // } +// // this replaces the prefixes in their entirety if provided +// let joinedPrefixes +// if (autogeneratedRoutePrefixes) { +// joinedPrefixes = autogeneratedRoutePrefixes.join(',') +// } +// if (autogeneratedRoutePrefixes === null) { +// joinedPrefixes = null +// } + +// // this replaces the pathroutes in their entirety if provided +// let pathRoutes +// if (autogeneratedPathRoutes) { +// pathRoutes = JSON.stringify(autogeneratedPathRoutes) +// } +// if (autogeneratedPathRoutes === null) { +// pathRoutes = null +// } diff --git a/services/api/src/resources/routes/sql.ts b/services/api/src/resources/routes/sql.ts index ea214b698f..b8beae075c 100644 --- a/services/api/src/resources/routes/sql.ts +++ b/services/api/src/resources/routes/sql.ts @@ -1,42 +1,35 @@ import { knex } from '../../util/db'; export const Sql = { - selectRouteByID: (id: number) => - knex('routes') - .where('id', '=', id) - .toString(), - selectRoutesByEnvironmentID: (id: number) => - knex('routes') - .where('environment', id) - .orderBy('id', 'desc') - .toString(), - selectRoutesByDomainAndEnvironmentID: (domain: string, id: number) => - knex('routes') - .where('environment', '=', id) - .andWhere('domain', '=', domain) - .orderBy('id', 'desc') - .toString(), - selectRouteAlternativeDomainsByRouteID: (id: number) => - knex('routes_alternate_domain') - .select('domain') - .where('route_id', '=', id) - .toString(), - selectPathRoutesByRouteID: (id: number) => - knex('routes_path_routes') - .where('route_id', '=', id) - .toString(), - selectRouteByDomainAndProjectID: (domain: string, project: number) => - knex('routes') - .where('project', '=', project) - .andWhere('domain', '=', domain) - .toString(), - selectRouteAlternativeDomainsByDomainAndProjectID: (domain: string, project: number) => - knex('routes_alternate_domain') - .select('routes_alternate_domain.id','routes_alternate_domain.domain','routes.project','routes.environment') - .join('routes', 'routes_alternate_domain.route_id', 'routes.id') - .where('routes.project', '=', project) - .andWhere('routes_alternate_domain.domain', '=', domain) - .toString(), + selectRouteByID: (id: number) => knex('routes') + .where('id', '=', id) + .toString(), + selectRoutesByEnvironmentID: (id: number) => knex('routes') + .where('environment', id) + .orderBy('id', 'desc') + .toString(), + selectRoutesByDomainAndEnvironmentID: (domain: string, id: number) => knex('routes') + .where('environment', '=', id) + .andWhere('domain', '=', domain) + .orderBy('id', 'desc') + .toString(), + selectRouteAlternativeDomainsByRouteID: (id: number) => knex('routes_alternate_domain') + .select('domain') + .where('route_id', '=', id) + .toString(), + selectPathRoutesByRouteID: (id: number) => knex('routes_path_routes') + .where('route_id', '=', id) + .toString(), + selectRouteByDomainAndProjectID: (domain: string, project: number) => knex('routes') + .where('project', '=', project) + .andWhere('domain', '=', domain) + .toString(), + selectRouteAlternativeDomainsByDomainAndProjectID: (domain: string, project: number) => knex('routes_alternate_domain') + .select('routes_alternate_domain.id', 'routes_alternate_domain.domain', 'routes.project', 'routes.environment') + .join('routes', 'routes_alternate_domain.route_id', 'routes.id') + .where('routes.project', '=', project) + .andWhere('routes_alternate_domain.domain', '=', domain) + .toString(), insertRoute: ({ id, domain, @@ -73,148 +66,129 @@ export const Sql = { hstsMaxAge?: number, monitoringPath?: string, disableRequestVerification?: boolean, - }) => - knex('routes') - .insert({ - id, - domain, - environment, - project, - service, - source, - pathRoutes, - primary, - type, - tlsAcme, - insecure, - hstsEnabled, - hstsPreload, - hstsIncludeSubdomains, - hstsMaxAge, - monitoringPath, - disableRequestVerification, - }) - .toString(), + }) => knex('routes') + .insert({ + id, + domain, + environment, + project, + service, + source, + pathRoutes, + primary, + type, + tlsAcme, + insecure, + hstsEnabled, + hstsPreload, + hstsIncludeSubdomains, + hstsMaxAge, + monitoringPath, + disableRequestVerification, + }) + .toString(), insertRouteAlternativeDomain: ({ rid, domain, }: { rid: number, domain: string, - }) => - knex('routes_alternate_domain') - .insert({ - rid, - domain, - }) - .toString(), - deleteRoute: (id: number) => - knex('routes') - .where('id', id) - .del() - .toString(), - deleteRouteAlternativeDomain: (id: number) => - knex('routes_alternate_domain') - .where('id', id) - .del() - .toString(), - deleteRoutesAlternativeDomains: (id: number) => - knex('routes_alternate_domain') - .where('route_id', id) - .del() - .toString(), + }) => knex('routes_alternate_domain') + .insert({ + rid, + domain, + }) + .toString(), + deleteRoute: (id: number) => knex('routes') + .where('id', id) + .del() + .toString(), + deleteRouteAlternativeDomain: (id: number) => knex('routes_alternate_domain') + .where('id', id) + .del() + .toString(), + deleteRoutesAlternativeDomains: (id: number) => knex('routes_alternate_domain') + .where('route_id', id) + .del() + .toString(), insertRouteAnnotation: ({ routeId, key, - value + value, }: { routeId: number, key: string, value: string, - }) => - knex('routes_annotations') - .insert({ - routeId, - key, - value, - }) - .toString(), - selectRouteAnnotationsByRouteID: (id: number) => - knex('routes_annotations') - .where('route_id', '=', id) - .toString(), - deleteRoutesAnnotation: (rid: number, key: string) => - knex('routes_annotations') - .where('route_id', rid) - .andWhere('key', key) - .del() - .toString(), - deleteRoutesAnnotations: (rid: number) => - knex('routes_annotations') - .where('route_id', rid) - .del() - .toString(), - updateRoute: ({ id, patch }: { id: number, patch: { [key: string]: any } }) => - knex('routes') - .where('id', id) - .update(patch) - .toString(), - unsetEnvironmentPrimaryRoute: (environmentId: number, projectId: number) => - knex - .into('routes') - .where('environment', environmentId) - .andWhere('project', projectId) - .update({ primary: false }) - .toString(), - removeRouteFromEnvironment: (routeId: number) => - knex('routes') - .update('pathRoutes', null) - .update('service', null) - .update('environment', null) - .update('type', 'standard') - .update('primary', false) - .where('id', routeId) - .toString(), - removeAllRoutesFromEnvironment: (environmentId: number) => - knex('routes') - .update('pathRoutes', null) - .update('service', null) - .update('environment', null) - .update('primary', false) - .where('environment', environmentId) - .toString(), - deleteAutogeneratedRoutesForEnvironment: (environmentId: number) => - knex('routes') - .where('environment', environmentId) - .andWhere('type', 'autogenerated') - .del() - .toString(), - deleteLagoonYAMLRoutesForEnvironment: (environmentId: number) => - knex('routes') - .where('environment', environmentId) - .andWhere('type', 'yaml') - .del() - .toString(), - selectAutogeneratedRouteConfigByEnvironmentID: (environmentId: number) => - knex('routes_autogenerated_configuration') - .where('type', 'environment') - .andWhere('type_id', environmentId) - .toString(), - deleteAutogeneratedRouteConfigForEnvironment: (environmentId: number) => - knex('routes_autogenerated_configuration') - .where('type', 'environment') - .andWhere('type_id', environmentId) - .del() - .toString(), - selectAutogeneratedRouteConfigByProjectID: (projectId: number) => - knex('routes_autogenerated_configuration') - .where('type', 'project') - .andWhere('type_id', projectId) - .toString(), - deleteAutogeneratedRouteConfigForProject: (projectId: number) => - knex('routes_autogenerated_configuration') - .where('type', 'project') - .andWhere('type_id', projectId) - .del() - .toString(), + }) => knex('routes_annotations') + .insert({ + routeId, + key, + value, + }) + .toString(), + selectRouteAnnotationsByRouteID: (id: number) => knex('routes_annotations') + .where('route_id', '=', id) + .toString(), + deleteRoutesAnnotation: (rid: number, key: string) => knex('routes_annotations') + .where('route_id', rid) + .andWhere('key', key) + .del() + .toString(), + deleteRoutesAnnotations: (rid: number) => knex('routes_annotations') + .where('route_id', rid) + .del() + .toString(), + updateRoute: ({ id, patch }: { id: number, patch: { [key: string]: any } }) => knex('routes') + .where('id', id) + .update(patch) + .toString(), + unsetEnvironmentPrimaryRoute: (environmentId: number, projectId: number) => knex + .into('routes') + .where('environment', environmentId) + .andWhere('project', projectId) + .update({ primary: false }) + .toString(), + removeRouteFromEnvironment: (routeId: number) => knex('routes') + .update('pathRoutes', null) + .update('service', null) + .update('environment', null) + .update('type', 'standard') + .update('primary', false) + .where('id', routeId) + .toString(), + removeAllRoutesFromEnvironment: (environmentId: number) => knex('routes') + .update('pathRoutes', null) + .update('service', null) + .update('environment', null) + .update('primary', false) + .where('environment', environmentId) + .toString(), + deleteAutogeneratedRoutesForEnvironment: (environmentId: number) => knex('routes') + .where('environment', environmentId) + .andWhere('type', 'autogenerated') + .del() + .toString(), + deleteLagoonYAMLRoutesForEnvironment: (environmentId: number) => knex('routes') + .where('environment', environmentId) + .andWhere('type', 'yaml') + .del() + .toString(), + selectAutogeneratedRouteConfigByEnvironmentID: (environmentId: number) => knex('routes_autogenerated_configuration') + .where('type', 'environment') + .andWhere('type_id', environmentId) + .toString(), + deleteAutogeneratedRouteConfigForEnvironment: (environmentId: number) => knex('routes_autogenerated_configuration') + .where('type', 'environment') + .andWhere('type_id', environmentId) + .del() + .toString(), + selectAutogeneratedRouteConfigByProjectID: (projectId: number) => knex('routes_autogenerated_configuration') + .where('type', 'project') + .andWhere('type_id', projectId) + .toString(), + deleteAutogeneratedRouteConfigForProject: (projectId: number) => knex('routes_autogenerated_configuration') + .where('type', 'project') + .andWhere('type_id', projectId) + .del() + .toString(), }; diff --git a/services/api/src/resources/sshKey/helpers.ts b/services/api/src/resources/sshKey/helpers.ts new file mode 100644 index 0000000000..70af6757a6 --- /dev/null +++ b/services/api/src/resources/sshKey/helpers.ts @@ -0,0 +1,35 @@ +const VALID_KEY_TYPES = [ + 'ssh-rsa', + 'ssh-dss', + 'ssh-ed25519', + 'ecdsa-sha2-nistp256', + 'ecdsa-sha2-nistp384', + 'ecdsa-sha2-nistp521', +]; + +// Minimum length of the base64-encoded key material for it to represent a +// plausible real key (shortest valid key is ed25519 at ~68 bytes → ~91 base64 chars). +const MIN_KEY_DATA_LENGTH = 60; + +/** + * Validates an SSH public key string of the form " [comment]". + * Returns true only when the key type is recognised and the key data is long + * enough to represent a real key. + */ +export const validateSshKey = (sshKey: string): boolean => { + const parts = sshKey.trim().split(/\s+/); + if (parts.length < 2) { + return false; + } + const [keyType, keyData] = parts; + if (!VALID_KEY_TYPES.includes(keyType)) { + return false; + } + if (keyData.length < MIN_KEY_DATA_LENGTH) { + return false; + } + if (!/^[A-Za-z0-9+/]+=*$/.test(keyData)) { + return false; + } + return true; +}; diff --git a/services/api/src/resources/sshKey/resolvers.ts b/services/api/src/resources/sshKey/resolvers.ts index 349b5e0ad6..e1a68176e4 100644 --- a/services/api/src/resources/sshKey/resolvers.ts +++ b/services/api/src/resources/sshKey/resolvers.ts @@ -1,11 +1,11 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { query, isPatchEmpty } from '../../util/db'; import { Sql } from './sql'; -import { validateKey, generatePrivateKey as genpk } from '../../util/func'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { validateKey } from '../../util/func'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; import { EnvironmentModel } from '../../models/environment'; import { GroupModel } from '../../models/group'; import { UserModel, User } from '../../models/user'; @@ -15,10 +15,10 @@ const formatSshKey = ({ keyType, keyValue }) => `${keyType} ${keyValue}`; export const getUserSshKeys: ResolverFn = async ( { id: userId }, args, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { await hasPermission('ssh_key', 'view:user', { - users: [userId] + users: [userId], }); return query(sqlClientPool, Sql.selectSshKeysByUserId(userId)); @@ -27,62 +27,66 @@ export const getUserSshKeys: ResolverFn = async ( export const addSshKey: ResolverFn = async ( root, { - input: { id, name, publicKey, keyValue, keyType, user: userInput } + input: { + id, name, publicKey, keyValue, keyType, user: userInput, + }, + }, + { + sqlClientPool, hasPermission, models, userActivityLogger, }, - { sqlClientPool, hasPermission, models, userActivityLogger } ) => { const user = await models.UserModel.loadUserByIdOrEmail({ id: R.prop('id', userInput), - email: R.prop('email', userInput) + email: R.prop('email', userInput), }); await hasPermission('ssh_key', 'add', { - users: [user.id] + users: [user.id], }); - let keyFormatted = "" + let keyFormatted = ''; if (!publicKey) { keyType = keyType.replaceAll('_', '-').toLowerCase(); // handle key being sent as "ssh-rsa SSHKEY foo@bar.baz" as well as just the SSHKEY const keyValueParts = keyValue.split(' '); keyFormatted = formatSshKey({ keyType, - keyValue: keyValueParts.length > 1 ? keyValueParts[1] : keyValue + keyValue: keyValueParts.length > 1 ? keyValueParts[1] : keyValue, }); } else { - keyFormatted = publicKey + keyFormatted = publicKey; } - const vkey = await validateKey(keyFormatted, "public") - if (!vkey['sha256fingerprint']) { + const vkey = await validateKey(keyFormatted, 'public'); + if (!vkey.sha256fingerprint) { throw new Error('Invalid SSH key format! Please verify keyType + keyValue'); } let insertId: number; try { - ({insertId} = await query( + ({ insertId } = await query( sqlClientPool, Sql.insertSshKey({ id, name, - keyValue: vkey['value'], - keyType: vkey['type'], - keyFingerprint: vkey['sha256fingerprint'] - }) + keyValue: vkey.value, + keyType: vkey.type, + keyFingerprint: vkey.sha256fingerprint, + }), )); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error adding SSH key. Key already exists.` + 'Error adding SSH key. Key already exists.', ); } else { throw new Error(error.message); } - }; + } await query( sqlClientPool, - Sql.addSshKeyToUser({ sshKeyId: insertId, userId: user.id }) + Sql.addSshKeyToUser({ sshKeyId: insertId, userId: user.id }), ); const rows = await query(sqlClientPool, Sql.selectSshKey(insertId)); @@ -93,13 +97,13 @@ export const addSshKey: ResolverFn = async ( details: user.email, }, linkedResource: { - id: id, + id, type: AuditType.SSHKEY, - details: vkey['sha256fingerprint'] + details: vkey.sha256fingerprint, }, }; - var emailDetails = await getSshEmailDetails('api:addSshKey', name, models, user); + const emailDetails = await getSshEmailDetails('api:addSshKey', name, models, user); userActivityLogger(`User added ssh key '${name}'`, { project: '', @@ -108,17 +112,17 @@ export const addSshKey: ResolverFn = async ( input: { id, name, - keyValue: vkey['value'], - keyType: vkey['type'], - keyFingerprint: vkey['sha256fingerprint'] + keyValue: vkey.value, + keyType: vkey.type, + keyFingerprint: vkey.sha256fingerprint, }, data: { sshKeyId: insertId, - user + user, }, ...auditLog, ...emailDetails, - } + }, }); return R.prop(0, rows); @@ -130,16 +134,20 @@ export const updateSshKey: ResolverFn = async ( input: { id, patch, - patch: { name, publicKey, keyType, keyValue } - } + patch: { + name, publicKey, keyType, keyValue, + }, + }, + }, + { + sqlClientPool, hasPermission, models, userActivityLogger, }, - { sqlClientPool, hasPermission, models, userActivityLogger } ) => { const perms = await query(sqlClientPool, Sql.selectUserIdsBySshKeyId(id)); const userIds = R.map(R.prop('usid'), perms); await hasPermission('ssh_key', 'update', { - users: userIds + users: userIds, }); const user = await models.UserModel.loadUserByIdOrEmail({ @@ -150,23 +158,23 @@ export const updateSshKey: ResolverFn = async ( throw new Error('Input patch requires at least 1 attribute'); } - let keyFormatted = "" + let keyFormatted = ''; if (!publicKey) { keyType = keyType.replaceAll('_', '-').toLowerCase(); // handle key being sent as "ssh-rsa SSHKEY foo@bar.baz" as well as just the SSHKEY const keyValueParts = keyValue.split(' '); keyFormatted = formatSshKey({ keyType, - keyValue: keyValueParts.length > 1 ? keyValueParts[1] : keyValue + keyValue: keyValueParts.length > 1 ? keyValueParts[1] : keyValue, }); } else { - keyFormatted = publicKey + keyFormatted = publicKey; } - const vkey = await validateKey(keyFormatted, "public") - if (!vkey['sha256fingerprint']) { + const vkey = await validateKey(keyFormatted, 'public'); + if (!vkey.sha256fingerprint) { throw new Error( - 'Invalid SSH key format! Please verify keyType + keyValue' + 'Invalid SSH key format! Please verify keyType + keyValue', ); } @@ -177,21 +185,21 @@ export const updateSshKey: ResolverFn = async ( id, patch: { name, - keyType: vkey['type'], - keyValue: vkey['value'], - keyFingerprint: vkey['sha256fingerprint'] - } - }) + keyType: vkey.type, + keyValue: vkey.value, + keyFingerprint: vkey.sha256fingerprint, + }, + }), ); - } catch(error) { - if(error.text.includes("Duplicate entry")){ + } catch (error) { + if (error.text.includes('Duplicate entry')) { throw new Error( - `Error updating SSH key. Key already exists.` + 'Error updating SSH key. Key already exists.', ); } else { throw new Error(error.message); } - }; + } const rows = await query(sqlClientPool, Sql.selectSshKey(id)); @@ -202,13 +210,13 @@ export const updateSshKey: ResolverFn = async ( details: user.email, }, linkedResource: { - id: id, + id, type: AuditType.SSHKEY, - details: vkey['sha256fingerprint'] + details: vkey.sha256fingerprint, }, }; - var emailDetails = await getSshEmailDetails('api:updateSshKey', name, models, user); + const emailDetails = await getSshEmailDetails('api:updateSshKey', name, models, user); userActivityLogger(`User updated ssh key '${id}'`, { project: '', event: 'api:updateSshKey', @@ -216,7 +224,7 @@ export const updateSshKey: ResolverFn = async ( patch, ...auditLog, ...emailDetails, - } + }, }); return R.prop(0, rows); @@ -225,18 +233,20 @@ export const updateSshKey: ResolverFn = async ( export const deleteSshKey: ResolverFn = async ( root, { input: { name } }, - { sqlClientPool, hasPermission, models, userActivityLogger } + { + sqlClientPool, hasPermission, models, userActivityLogger, + }, ) => { // Map from sshKey name to id and throw on several error cases const skidResult = await query( sqlClientPool, - Sql.selectSshKeyIdByName(name) + Sql.selectSshKeyIdByName(name), ); const amount = R.length(skidResult); if (amount > 1) { throw new Error( - `Multiple sshKey candidates for '${name}' (${amount} found). Do nothing.` + `Multiple sshKey candidates for '${name}' (${amount} found). Do nothing.`, ); } @@ -248,26 +258,20 @@ export const deleteSshKey: ResolverFn = async ( const perms = await query( sqlClientPool, - Sql.selectUserIdsBySshKeyId(skid) + Sql.selectUserIdsBySshKeyId(skid), ); const userIds = R.map(R.prop('usid'), perms); await hasPermission('ssh_key', 'delete', { - users: userIds + users: userIds, }); const user = await models.UserModel.loadUserByIdOrEmail({ id: userIds[0], }); - let res = await query( - sqlClientPool, - Sql.deleteUserSshKeyByKeyId(skid) - ); - res = await query( - sqlClientPool, - Sql.deleteSshKeyByKeyId(skid) - ); + await query(sqlClientPool, Sql.deleteUserSshKeyByKeyId(skid)); + await query(sqlClientPool, Sql.deleteSshKeyByKeyId(skid)); const auditLog: AuditLog = { resource: { @@ -280,22 +284,22 @@ export const deleteSshKey: ResolverFn = async ( type: AuditType.SSHKEY, }, }; - var emailDetails = await getSshEmailDetails('api:deleteSshKey', "", models, user); + const emailDetails = await getSshEmailDetails('api:deleteSshKey', '', models, user); userActivityLogger(`User deleted ssh key '${name}'`, { project: '', event: 'api:deleteSshKey', payload: { input: { - name + name, }, data: { ssh_key_name: name, ssh_key_id: skid, - user: userIds + user: userIds, }, ...auditLog, ...emailDetails, - } + }, }); return 'success'; @@ -304,27 +308,23 @@ export const deleteSshKey: ResolverFn = async ( export const deleteSshKeyById: ResolverFn = async ( root, { input: { id } }, - { sqlClientPool, hasPermission, models, userActivityLogger } + { + sqlClientPool, hasPermission, models, userActivityLogger, + }, ) => { const perms = await query(sqlClientPool, Sql.selectUserIdsBySshKeyId(id)); const userIds = R.map(R.prop('usid'), perms); await hasPermission('ssh_key', 'delete', { - users: userIds + users: userIds, }); const user = await models.UserModel.loadUserByIdOrEmail({ id: userIds[0], }); - let res = await query( - sqlClientPool, - Sql.deleteUserSshKeyByKeyId(id) - ); - res = await query( - sqlClientPool, - Sql.deleteSshKeyByKeyId(id) - ); + await query(sqlClientPool, Sql.deleteUserSshKeyByKeyId(id)); + await query(sqlClientPool, Sql.deleteSshKeyByKeyId(id)); // TODO: Check rows for success @@ -339,21 +339,21 @@ export const deleteSshKeyById: ResolverFn = async ( type: AuditType.SSHKEY, }, }; - var emailDetails = await getSshEmailDetails('api:deleteSshKeyById', "", models, user); + const emailDetails = await getSshEmailDetails('api:deleteSshKeyById', '', models, user); userActivityLogger(`User deleted ssh key with id '${id}'`, { project: '', event: 'api:deleteSshKeyById', payload: { input: { - id + id, }, data: { ssh_key_id: id, - user: userIds + user: userIds, }, ...auditLog, ...emailDetails, - } + }, }); return 'success'; @@ -362,10 +362,10 @@ export const deleteSshKeyById: ResolverFn = async ( // This function retrieves email details for SSH key actions based on user preferences // it's really a convenience function to avoid code duplication async function getSshEmailDetails(action, keyname, models: { UserModel: UserModel; GroupModel: GroupModel; EnvironmentModel: EnvironmentModel; }, user: User) { - var emailDetails = {}; + let emailDetails = {}; try { - let dbUserDetails = await models.UserModel.getFullUserDetails(user); - if (dbUserDetails && dbUserDetails['optEmailSshkey'] == true) { + const dbUserDetails = await models.UserModel.getFullUserDetails(user) as any; + if (dbUserDetails && dbUserDetails.optEmailSshkey === true) { emailDetails = { userActionEmailDetails: { Name: user.email, @@ -381,9 +381,8 @@ async function getSshEmailDetails(action, keyname, models: { UserModel: UserMode user.id, action, {}, - `Error while trying to get full user DB details for user(id|email) (${user.id}|${user.email}): error: ${e.message}` + `Error while trying to get full user DB details for user(id|email) (${user.id}|${user.email}): error: ${e.message}`, ); } return emailDetails; } - diff --git a/services/api/src/resources/sshKey/sql.ts b/services/api/src/resources/sshKey/sql.ts index aa9f18f8e6..50998422eb 100644 --- a/services/api/src/resources/sshKey/sql.ts +++ b/services/api/src/resources/sshKey/sql.ts @@ -1,46 +1,38 @@ import { knex } from '../../util/db'; export const Sql = { - selectSshKey: (id: number) => - knex('ssh_key') - .where('id', '=', id) - .toString(), - selectSshKeyIdByName: (name: string) => - knex('ssh_key') - .where('name', '=', name) - .select('id') - .toString(), - selectSshKeysByUserId: (userId: number) => - knex('ssh_key as sk') - .join('user_ssh_key as usk', 'sk.id', '=', 'usk.skid') - .where('usk.usid', '=', userId) - .toString(), - selectUserIdsBySshKeyId: (keyId: number) => - knex('ssh_key as sk') - .select('usk.usid') - .join('user_ssh_key as usk', 'sk.id', '=', 'usk.skid') - .where('usk.skid', '=', keyId) - .toString(), - selectUserIdsBySshKeyFingerprint: (fingerprint: string) => - knex('ssh_key as sk') - .select('usk.usid') - .join('user_ssh_key as usk', 'sk.id', '=', 'usk.skid') - .where('sk.key_fingerprint', '=', fingerprint) - .toString(), - selectSshKeyByFingerprint: (fingerprint: string) => - knex('ssh_key') - .where('key_fingerprint', '=', fingerprint) - .toString(), - deleteUserSshKeyByKeyId: (skid: number) => - knex('user_ssh_key') - .where('skid', skid) - .delete() - .toString(), - deleteSshKeyByKeyId: (skid: number) => - knex('ssh_key') - .where('id', skid) - .delete() - .toString(), + selectSshKey: (id: number) => knex('ssh_key') + .where('id', '=', id) + .toString(), + selectSshKeyIdByName: (name: string) => knex('ssh_key') + .where('name', '=', name) + .select('id') + .toString(), + selectSshKeysByUserId: (userId: number) => knex('ssh_key as sk') + .join('user_ssh_key as usk', 'sk.id', '=', 'usk.skid') + .where('usk.usid', '=', userId) + .toString(), + selectUserIdsBySshKeyId: (keyId: number) => knex('ssh_key as sk') + .select('usk.usid') + .join('user_ssh_key as usk', 'sk.id', '=', 'usk.skid') + .where('usk.skid', '=', keyId) + .toString(), + selectUserIdsBySshKeyFingerprint: (fingerprint: string) => knex('ssh_key as sk') + .select('usk.usid') + .join('user_ssh_key as usk', 'sk.id', '=', 'usk.skid') + .where('sk.key_fingerprint', '=', fingerprint) + .toString(), + selectSshKeyByFingerprint: (fingerprint: string) => knex('ssh_key') + .where('key_fingerprint', '=', fingerprint) + .toString(), + deleteUserSshKeyByKeyId: (skid: number) => knex('user_ssh_key') + .where('skid', skid) + .delete() + .toString(), + deleteSshKeyByKeyId: (skid: number) => knex('ssh_key') + .where('id', skid) + .delete() + .toString(), insertSshKey: ({ id, name, @@ -53,36 +45,31 @@ export const Sql = { keyValue: string, keyType: string, keyFingerprint: string, - }) => - knex('ssh_key') - .insert({ - id, - name, - key_value: keyValue, - key_type: keyType, - key_fingerprint: keyFingerprint, - }) - .toString(), + }) => knex('ssh_key') + .insert({ + id, + name, + key_value: keyValue, + key_type: keyType, + key_fingerprint: keyFingerprint, + }) + .toString(), addSshKeyToUser: ( - { sshKeyId, userId }: { sshKeyId: number, userId: string }) => - knex('user_ssh_key') - .insert({ - usid: userId, - skid: sshKeyId, - }) - .toString(), + { sshKeyId, userId }: { sshKeyId: number, userId: string }) => knex('user_ssh_key') + .insert({ + usid: userId, + skid: sshKeyId, + }) + .toString(), updateSshKey: ( - { id, patch }: { id: number, patch: { [key: string]: any } }) => - knex('ssh_key') - .where('id', '=', id) - .update(patch) - .toString(), - truncateSshKey: () => - knex('ssh_key') - .truncate() - .toString(), - truncateUserSshKey: () => - knex('user_ssh_key') - .truncate() - .toString(), + { id, patch }: { id: number, patch: { [key: string]: any } }) => knex('ssh_key') + .where('id', '=', id) + .update(patch) + .toString(), + truncateSshKey: () => knex('ssh_key') + .truncate() + .toString(), + truncateUserSshKey: () => knex('user_ssh_key') + .truncate() + .toString(), }; diff --git a/services/api/src/resources/sshKey/sshKey.test.js b/services/api/src/resources/sshKey/sshKey.test.js index 45a26ced06..f637d39464 100644 --- a/services/api/src/resources/sshKey/sshKey.test.js +++ b/services/api/src/resources/sshKey/sshKey.test.js @@ -1,5 +1,6 @@ +/* eslint-disable import/no-unresolved */ const { Sql } = require('./sql'); -const { validateSshKey } = require('./index'); +const { validateSshKey } = require('./helpers'); describe('Sql', () => { describe('updateSshKey', () => { diff --git a/services/api/src/resources/task/advancedtasktoolbox.test.ts b/services/api/src/resources/task/advancedtasktoolbox.test.ts index 4c4679d297..2593e7ff5d 100644 --- a/services/api/src/resources/task/advancedtasktoolbox.test.ts +++ b/services/api/src/resources/task/advancedtasktoolbox.test.ts @@ -1,9 +1,6 @@ -import { IKeycloakAuthAttributes, KeycloakUnauthorizedError } from '../../util/auth'; import * as R from 'ramda'; -import { deploymentSubscriber } from '../deployment/resolvers'; +import { IKeycloakAuthAttributes, KeycloakUnauthorizedError } from '../../util/auth'; import { advancedTaskFunctionFactory } from './advancedtasktoolbox'; -import { sqlClientPool } from '../../clients/sqlClient'; - interface IPermissionsMockItem { resource: string, @@ -12,69 +9,59 @@ interface IPermissionsMockItem { } const areIKeycloakAuthAttributesEqual = (a: IKeycloakAuthAttributes, b: IKeycloakAuthAttributes) => { - const userSort = R.partial(R.sort, [(a, b) => { return a - b;}]); - return R.equals(a.project, b.project) && R.equals(a.group, b.group) && - R.equals(userSort(a.users || []), userSort(b.users || [])); -} + const userSort = R.partial(R.sort, [(a: any, b: any) => a - b]); + return R.equals(a.project, b.project) && R.equals(a.group, b.group) + && R.equals(userSort(a.users || []), userSort(b.users || [])); +}; -//Mock out the hasPermissions function +// Mock out the hasPermissions function // requires sets of `resource, scope, attributes: IKeycloakAuthAttributes = {}` to define permissions -const mockHasPermission = (permissions: Array) => { - return async (resource, scope, attributes: IKeycloakAuthAttributes = {}) => { - let match = false; - permissions.forEach(element => { - if(element.resource == resource && - scope == element.scope && - areIKeycloakAuthAttributesEqual(element.attributes, attributes) - ) { - match = true; - } - }); - if(match) { return true; } - throw new KeycloakUnauthorizedError(`Unauthorized: You don't have permission to "${scope}" on "${resource}".`); +const mockHasPermission = (permissions: Array) => async (resource, scope, attributes: IKeycloakAuthAttributes = {}) => { + let match = false; + permissions.forEach(element => { + if (element.resource === resource + && scope === element.scope + && areIKeycloakAuthAttributesEqual(element.attributes, attributes) + ) { + match = true; } -} - - + }); + if (match) { return true; } + throw new KeycloakUnauthorizedError(`Unauthorized: You don't have permission to "${scope}" on "${resource}".`); +}; describe('advancedtasktoolbox', () => { - describe('canUserSeeTaskDefinition', () => { - - let environmentById = jest.fn((id: number) => { - if(id == 1) { - return {project: 1}; - } - return {project: 2}; - }); + describe('canUserSeeTaskDefinition', () => { + const environmentById = jest.fn((id: number) => { + if (id === 1) { + return { project: 1 }; + } + return { project: 2 }; + }); - let environmentHelpers = { - getEnvironmentById: environmentById, - }; + const environmentHelpers = { + getEnvironmentById: environmentById, + }; - //This user has permission to view tasks on - let hasPermissions = mockHasPermission([{resource: 'task', scope: 'view', attributes: {project: 1}}]) - let ath = advancedTaskFunctionFactory({}, hasPermissions, {}, {}, environmentHelpers, {}, {}); + // This user has permission to view tasks on + const hasPermissions = mockHasPermission([{ resource: 'task', scope: 'view', attributes: { project: 1 } }]); + const ath = advancedTaskFunctionFactory({}, hasPermissions, {}, {}, environmentHelpers, {}, {}); - test('test user is granted permission when invoking a project she has access to', () => { - return expect(ath.permissions.canUserSeeTaskDefinition({environment: 1})).resolves.toBe(true); - }); + test('test user is granted permission when invoking a project she has access to', () => expect(ath.permissions.canUserSeeTaskDefinition({ environment: 1 })).resolves.toBe(true)); - test('test user is denied permission to a project she doesnt have access to', () => { - return expect(ath.permissions.canUserSeeTaskDefinition({environment: 2})).resolves.toBe(false); - }); - }); + test('test user is denied permission to a project she doesnt have access to', () => expect(ath.permissions.canUserSeeTaskDefinition({ environment: 2 })).resolves.toBe(false)); + }); }); - -//Let's quickly ensure our test functions are working as expected +// Let's quickly ensure our test functions are working as expected describe('testSystemsMetaTest', () => { - const usersPermissions = [ - {resource: 'task', scope: 'view', attributes: {users: [1,2,3]}}, - {resource: 'nothing', scope: 'whatever', attributes: {}}, - ]; + const usersPermissions = [ + { resource: 'task', scope: 'view', attributes: { users: [1, 2, 3] } }, + { resource: 'nothing', scope: 'whatever', attributes: {} }, + ]; - test('should match our users permissions when running haspermissions', () => { - let hasPermission = mockHasPermission(usersPermissions); - return expect(hasPermission('task', 'view', {users: [2,1,3]})).resolves.toBe(true); - }); + test('should match our users permissions when running haspermissions', () => { + const hasPermission = mockHasPermission(usersPermissions); + return expect(hasPermission('task', 'view', { users: [2, 1, 3] })).resolves.toBe(true); + }); }); diff --git a/services/api/src/resources/task/advancedtasktoolbox.ts b/services/api/src/resources/task/advancedtasktoolbox.ts index 1b769ae655..ece4b60388 100644 --- a/services/api/src/resources/task/advancedtasktoolbox.ts +++ b/services/api/src/resources/task/advancedtasktoolbox.ts @@ -2,89 +2,77 @@ import * as R from 'ramda'; import { query } from '../../util/db'; import { IKeycloakAuthAttributes, KeycloakUnauthorizedError } from '../../util/auth'; import { Sql } from './sql'; -import { Helpers } from './helpers'; import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; -import { Validators as envValidators } from '../environment/validators'; -import { - TaskRegistration, - newTaskRegistrationFromObject -} from './models/taskRegistration'; -import convertDateToMYSQLDateTimeFormat from '../../util/convertDateToMYSQLDateTimeFormat'; -import { sqlClientPool } from '../../clients/sqlClient'; - - export const advancedTaskFunctions = (sqlClientPool, models, hasPermission = null, adminScopes) => { - //Here we use partial application to generate a query runner - //This allows us to replace whatever is doing the running with some mocked object if we need. + // Here we use partial application to generate a query runner + // This allows us to replace whatever is doing the running with some mocked object if we need. - const queryRunner = R.partial(query, [sqlClientPool]); + const queryRunner = R.partial(query, [sqlClientPool]); - //Note, following the injection functionality above, we also pass through environment and project helpers that can be mocked. - return advancedTaskFunctionFactory(queryRunner, hasPermission, models, Sql, environmentHelpers(sqlClientPool), projectHelpers(sqlClientPool), adminScopes); -} + // Note, following the injection functionality above, we also pass through environment and project helpers that can be mocked. + return advancedTaskFunctionFactory(queryRunner, hasPermission, models, Sql, environmentHelpers(sqlClientPool), projectHelpers(sqlClientPool), adminScopes); +}; export const advancedTaskFunctionFactory = (queryRunner, hasPermission = null, models, Sql, environmentHelpers, projectHelpers, adminScopes) => { - - //This provides a reasonable alternative to simply throwing errors if permission checks fail. - const tryCatchHaspermission = async (resource, scope, attributes: IKeycloakAuthAttributes) => { - try { - await hasPermission(resource, scope, attributes); - } catch(e) { - if(e instanceof KeycloakUnauthorizedError) { - return false; - } - //if tihs isn't an expected exception, we have to rethrow; - throw e; + // This provides a reasonable alternative to simply throwing errors if permission checks fail. + const tryCatchHaspermission = async (resource, scope, attributes: IKeycloakAuthAttributes) => { + try { + await hasPermission(resource, scope, attributes); + } catch (e) { + if (e instanceof KeycloakUnauthorizedError) { + return false; } - return true; + // if tihs isn't an expected exception, we have to rethrow; + throw e; } + return true; + }; - return { - advancedTaskDefinitionById: async function(id) { - const rows = await queryRunner(Sql.selectAdvancedTaskDefinition(id)) - let taskDef = R.prop(0, rows); - taskDef.advancedTaskDefinitionArguments = await this.advancedTaskDefinitionArguments( - taskDef.id - ); - return taskDef; - }, - advancedTaskDefinitionArguments: async function(task_definition_id) { - const rows = await queryRunner(Sql.selectAdvancedTaskDefinitionArguments(task_definition_id)) - let taskDefArgs = rows; - return taskDefArgs; - }, - permissions: { - canUserSeeTaskDefinition: async(advancedTaskDefinition) => { - //either project, environment, or group will be - we have to run different checks for each possibility - if(advancedTaskDefinition.environment !== null) { - let env = await environmentHelpers.getEnvironmentById(advancedTaskDefinition.environment); - if (adminScopes.platformViewer) { - return true; - } - return await tryCatchHaspermission('task', 'view', { - project: env.project - }); - } else if (advancedTaskDefinition.project !== null) { - if (adminScopes.platformViewer) { - return true; - } - return await tryCatchHaspermission('task', 'view', { - project: advancedTaskDefinition.project - }); - } else if (advancedTaskDefinition.groupName !== null) { - - const group = await models.GroupModel.loadGroupByIdOrName({ - name: advancedTaskDefinition.groupName - }); - - return await tryCatchHaspermission('group', 'update', { - group: group.id - }); + return { + async advancedTaskDefinitionById(id) { + const rows = await queryRunner(Sql.selectAdvancedTaskDefinition(id)); + const taskDef = R.prop(0, rows); + taskDef.advancedTaskDefinitionArguments = await this.advancedTaskDefinitionArguments( + taskDef.id, + ); + return taskDef; + }, + async advancedTaskDefinitionArguments(task_definition_id) { + const rows = await queryRunner(Sql.selectAdvancedTaskDefinitionArguments(task_definition_id)); + const taskDefArgs = rows; + return taskDefArgs; + }, + permissions: { + canUserSeeTaskDefinition: async (advancedTaskDefinition) => { + // either project, environment, or group will be - we have to run different checks for each possibility + if (advancedTaskDefinition.environment !== null) { + const env = await environmentHelpers.getEnvironmentById(advancedTaskDefinition.environment); + if (adminScopes.platformViewer) { + return true; } - return false; - }, + return await tryCatchHaspermission('task', 'view', { + project: env.project, + }); + } if (advancedTaskDefinition.project !== null) { + if (adminScopes.platformViewer) { + return true; + } + return await tryCatchHaspermission('task', 'view', { + project: advancedTaskDefinition.project, + }); + } if (advancedTaskDefinition.groupName !== null) { + const group = await models.GroupModel.loadGroupByIdOrName({ + name: advancedTaskDefinition.groupName, + }); + + return await tryCatchHaspermission('group', 'update', { + group: group.id, + }); + } + return false; }, - }; + }, }; +}; diff --git a/services/api/src/resources/task/filters.ts b/services/api/src/resources/task/filters.ts index 4cadf93094..7c329493a8 100644 --- a/services/api/src/resources/task/filters.ts +++ b/services/api/src/resources/task/filters.ts @@ -1,26 +1,24 @@ -import * as R from 'ramda'; - export const Filters = { - filterAdminTasks: async (hasPermission, rows) => { - let adminTasks = false - // do a check of all returned tasks for any that are admin tasks - for (const row of rows) { - if (row.adminOnlyView == true) { - adminTasks = true; - break; - } - } + filterAdminTasks: async (hasPermission, rows) => { + let adminTasks = false; + // do a check of all returned tasks for any that are admin tasks + for (const row of rows) { + if (row.adminOnlyView === true) { + adminTasks = true; + break; + } + } - // if any of the tasks are admin tasks, check the user has admin permission - if (adminTasks) { - // we do this so we only check admin permission once, instead of on all tasks - try { - await hasPermission('project', 'viewAll'); - } catch (err) { - return rows.filter((row) => row.adminOnlyView != true); - } - } - // else we simply return the rows untouched - return rows; + // if any of the tasks are admin tasks, check the user has admin permission + if (adminTasks) { + // we do this so we only check admin permission once, instead of on all tasks + try { + await hasPermission('project', 'viewAll'); + } catch (_err) { + return rows.filter((row) => row.adminOnlyView !== true); + } } -} \ No newline at end of file + // else we simply return the rows untouched + return rows; + }, +}; diff --git a/services/api/src/resources/task/helpers.ts b/services/api/src/resources/task/helpers.ts index 3a5a237a99..08457426fb 100644 --- a/services/api/src/resources/task/helpers.ts +++ b/services/api/src/resources/task/helpers.ts @@ -1,15 +1,15 @@ import * as R from 'ramda'; import { Pool } from 'mariadb'; -import { asyncPipe, encodeJSONBase64 } from '@lagoon/commons/dist/util/func'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; -import { createTaskTask, createMiscTask } from '@lagoon/commons/dist/tasks'; +import { asyncPipe, encodeJSONBase64 } from '../../commons/util/func'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; +import { createTaskTask, createMiscTask } from '../../commons/tasks'; import { knex, query } from '../../util/db'; import { pubSub, EVENTS } from '../../clients/pubSub'; import { Sql } from './sql'; import { Sql as projectSql } from '../project/sql'; import { Sql as environmentSql } from '../environment/sql'; import { Helpers as environmentHelpers } from '../environment/helpers'; -import { TaskSourceType } from '@lagoon/commons/dist/types'; +import { TaskSourceType } from '../../commons/types'; import { HistoryRetentionEnforcer } from '../retentionpolicy/history'; import { getConfigFromEnv } from '../../util/config'; import { logger } from '../../loggers/logger'; @@ -31,7 +31,7 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('task', 'view', { - project: R.path(['0', 'pid'], rowsPerms) + project: R.path(['0', 'pid'], rowsPerms), }); } @@ -56,7 +56,7 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { adminOnlyView, execute, sourceUser, - sourceType + sourceType, }: { id?: number; name: string; @@ -110,13 +110,13 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { rows = await query( sqlClientPool, - environmentSql.selectEnvironmentById(taskData.environment) + environmentSql.selectEnvironmentById(taskData.environment), ); const environmentData = R.prop(0, rows); rows = await query( sqlClientPool, - projectSql.selectProject(environmentData.project) + projectSql.selectProject(environmentData.project), ); const projectData = R.prop(0, rows); @@ -124,8 +124,8 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { const routerPatternVarsInput = { projectName: projectData.name, - environmentId: environmentData.id - } + environmentId: environmentData.id, + }; const response = await fetch( `http://${getConfigFromEnv('SIDECAR_HANDLER_HOST', 'localhost')}:3333/environment/routerpatternvariables`, { @@ -139,10 +139,10 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { ); if (!response.ok) { const errorText = await response.text(); - logger.error(`Error getting variables for task ${environmentData.name}: ${errorText}`) + logger.error(`Error getting variables for task ${environmentData.name}: ${errorText}`); throw new Error(`Error getting variables for task ${environmentData.name}`); } - const resp = JSON.parse(await response.text()) + const resp = JSON.parse(await response.text()); // logger.info(`AAAAAAA1 ${resp.envVars}`) try { @@ -159,12 +159,12 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { '', 'api:addTask', { taskId: taskData.id }, - `*[${projectData.name}]* Task not initiated, reason: ${error}` + `*[${projectData.name}]* Task not initiated, reason: ${error}`, ); } // pass to the HistoryRetentionEnforcer to clean up tasks based on any retention policies - await HistoryRetentionEnforcer().cleanupTasks(projectData, environmentData) + await HistoryRetentionEnforcer().cleanupTasks(projectData, environmentData); return taskData; }, @@ -182,7 +182,7 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { image, payload = {}, remoteId, - execute, + execute: _execute, deployTokenInjection, projectKeyInjection, adminOnlyView, @@ -217,8 +217,7 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { ); const projectData = R.prop(0, rows); - - const queryresp = await query( + const queryresp = await query( sqlClientPool, Sql.insertTask({ id, @@ -244,26 +243,26 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { const { insertId } = queryresp; rows = await query(sqlClientPool, Sql.selectTask(insertId)); const taskData = R.prop(0, rows); - const ADVANCED_TASK_EVENT_TYPE = "task:advanced" + const ADVANCED_TASK_EVENT_TYPE = 'task:advanced'; taskData.id = taskData.id.toString(); - let jobSpec = { + const jobSpec = { task: taskData, project: projectData, environment: environmentData, advancedTask: { RunnerImage: image, JSONPayload: encodeJSONBase64(payload), - deployerToken: deployTokenInjection, //an admintask will have a deployer token and ssh key injected into it + deployerToken: deployTokenInjection, // an admintask will have a deployer token and ssh key injected into it sshKey: projectKeyInjection, - } - } + }, + }; const routerPatternVarsInput = { projectName: projectData.name, - environmentId: environmentData.id - } + environmentId: environmentData.id, + }; const response = await fetch( `http://${getConfigFromEnv('SIDECAR_HANDLER_HOST', 'localhost')}:3333/environment/routerpatternvariables`, { @@ -277,10 +276,10 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { ); if (!response.ok) { const errorText = await response.text(); - logger.error(`Error getting variables for task ${environmentData.name}: ${errorText}`) + logger.error(`Error getting variables for task ${environmentData.name}: ${errorText}`); throw new Error(`Error getting variables for task ${environmentData.name}`); } - const resp = JSON.parse(await response.text()) + const resp = JSON.parse(await response.text()); try { await createMiscTask( @@ -288,8 +287,8 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { key: ADVANCED_TASK_EVENT_TYPE, data: jobSpec, envVars: resp.envVars, - } - ) + }, + ); } catch (error) { sendToLagoonLogs( 'error', @@ -309,67 +308,67 @@ export const Helpers = (sqlClientPool: Pool, hasPermission, adminScopes) => { const hasName = R.both(R.has('taskName'), R.propSatisfies(notEmpty, 'taskName')); const hasEnvironment = R.both( R.has('environment'), - R.propSatisfies(notEmpty, 'environment') + R.propSatisfies(notEmpty, 'environment'), ); // @ts-ignore const hasNameAndEnvironment = R.both(hasName, hasEnvironment); const taskFromId = asyncPipe( - R.prop('id'), - getTaskById, - task => { - if (!task) { - throw new Error('No matching task found'); - } - - return task; + R.prop('id'), + getTaskById, + task => { + if (!task) { + throw new Error('No matching task found'); } + + return task; + }, + ); + + const taskFromNameEnv = async input => { + const environments = await environmentHelpers( + sqlClientPool, + ).getEnvironmentsByEnvironmentInput(R.prop('environment', input)); + const activeEnvironments = R.filter( + R.propEq('0000-00-00 00:00:00', 'deleted'), + environments, ); - const taskFromNameEnv = async input => { - const environments = await environmentHelpers( - sqlClientPool - ).getEnvironmentsByEnvironmentInput(R.prop('environment', input)); - const activeEnvironments = R.filter( - R.propEq('deleted', '0000-00-00 00:00:00'), - environments - ); - - if (activeEnvironments.length != 1) { - throw new Error('No matching environment found'); - } + if (activeEnvironments.length !== 1) { + throw new Error('No matching environment found'); + } - const environment = R.prop(0, activeEnvironments); + const environment = R.prop(0, activeEnvironments); - const rows = await query( - sqlClientPool, - Sql.selectTaskByNameAndEnvironment( - R.prop('taskName', input), - environment.id - ) - ); + const rows = await query( + sqlClientPool, + Sql.selectTaskByNameAndEnvironment( + R.prop('taskName', input), + environment.id, + ), + ); - if (!R.prop(0, rows)) { - throw new Error('No matching task found'); - } + if (!R.prop(0, rows)) { + throw new Error('No matching task found'); + } - return R.prop(0, rows); - }; + return R.prop(0, rows); + }; - return R.cond([ - [hasId, taskFromId], - // @ts-ignore - [hasNameAndEnvironment, taskFromNameEnv], - [ - R.T, - () => { - throw new Error( - 'Must provide task (id) or (name and environment)' - ); - } - ] + return R.cond([ + [hasId, taskFromId], // @ts-ignore - ])(taskInput); - } + [hasNameAndEnvironment, taskFromNameEnv], + [ + R.T, + () => { + throw new Error( + 'Must provide task (id) or (name and environment)', + ); + }, + ], + // @ts-ignore + ])(taskInput); + }, }; }; diff --git a/services/api/src/resources/task/models/advancedTaskDefinitionArgument.ts b/services/api/src/resources/task/models/advancedTaskDefinitionArgument.ts index ed303f42f8..c3fa72c4a2 100644 --- a/services/api/src/resources/task/models/advancedTaskDefinitionArgument.ts +++ b/services/api/src/resources/task/models/advancedTaskDefinitionArgument.ts @@ -1,155 +1,149 @@ -import { query } from '../../../util/db'; import * as R from 'ramda'; +import { query } from '../../../util/db'; export class ArgumentBase { - async validateInput(input): Promise { - return true; - } - - public static typeName() { - return "BASE"; - } + async validateInput(_input): Promise { + return true; + } - public async getArgumentRange() { - return []; - } + public static typeName() { + return 'BASE'; + } + public async getArgumentRange() { + return []; + } } export class EnvironmentSourceArgument extends ArgumentBase { + protected sqlClientPool; + + protected environmentId; + + protected environmentNameList = []; + + constructor(sqlClientPool, environmentId) { + super(); + this.sqlClientPool = sqlClientPool; + this.environmentId = environmentId; + } - protected sqlClientPool; - protected environmentId; - protected environmentNameList = []; - - constructor(sqlClientPool, environmentId) { - super(); - this.sqlClientPool = sqlClientPool; - this.environmentId = environmentId; - } - - public static typeName() { - return "ENVIRONMENT_SOURCE_NAME"; - } - - public async getArgumentRange() { - await this.loadEnvNames(); - return this.environmentNameList; - } - - protected async loadEnvNames() { - const rows = await query( - this.sqlClientPool, - `select e.name as name from environment as e inner join environment as p on e.project = p.project where p.id = ${this.environmentId}` - ); - this.environmentNameList = R.pluck('name')(rows); - } - - /** + public static typeName() { + return 'ENVIRONMENT_SOURCE_NAME'; + } + + public async getArgumentRange() { + await this.loadEnvNames(); + return this.environmentNameList; + } + + protected async loadEnvNames() { + const rows = await query( + this.sqlClientPool, + `select e.name as name from environment as e inner join environment as p on e.project = p.project where p.id = ${this.environmentId}`, + ); + this.environmentNameList = R.pluck('name')(rows) as string[]; + } + + /** * * @param input Environment name * @returns boolean */ - async validateInput(input): Promise { - await this.loadEnvNames(); - return this.environmentNameList.includes(input); - } + async validateInput(input): Promise { + await this.loadEnvNames(); + return this.environmentNameList.includes(input); + } } - export class OtherEnvironmentSourceNamesArgument extends ArgumentBase { + protected sqlClientPool; + + protected environmentId; + + protected environmentNameList = []; - protected sqlClientPool; - protected environmentId; - protected environmentNameList = []; - - constructor(sqlClientPool, environmentId) { - super(); - this.sqlClientPool = sqlClientPool; - this.environmentId = environmentId; - } - - public static typeName() { - return "ENVIRONMENT_SOURCE_NAME_EXCLUDE_SELF"; - } - - public async getArgumentRange() { - await this.loadEnvNames(); - return this.environmentNameList; - } - - protected async loadEnvNames() { - const rows = await query( - this.sqlClientPool, - `select e.name as name from environment as e inner join environment as p on e.project = p.project where p.id = ${this.environmentId} and e.id != ${this.environmentId}` - ); - this.environmentNameList = R.pluck('name')(rows); - } - - /** + constructor(sqlClientPool, environmentId) { + super(); + this.sqlClientPool = sqlClientPool; + this.environmentId = environmentId; + } + + public static typeName() { + return 'ENVIRONMENT_SOURCE_NAME_EXCLUDE_SELF'; + } + + public async getArgumentRange() { + await this.loadEnvNames(); + return this.environmentNameList; + } + + protected async loadEnvNames() { + const rows = await query( + this.sqlClientPool, + `select e.name as name from environment as e inner join environment as p on e.project = p.project where p.id = ${this.environmentId} and e.id != ${this.environmentId}`, + ); + this.environmentNameList = R.pluck('name')(rows) as string[]; + } + + /** * * @param input Environment name * @returns boolean */ - async validateInput(input): Promise { - await this.loadEnvNames(); - return this.environmentNameList.includes(input); - } + async validateInput(input): Promise { + await this.loadEnvNames(); + return this.environmentNameList.includes(input); + } } - export class StringArgument extends ArgumentBase { + public static typeName() { + return 'STRING'; + } - public static typeName() { - return "STRING"; - } - - async validateInput(input): Promise { - return true; - } + async validateInput(_input): Promise { + return true; + } - public async getArgumentRange() { - return null; - } + public async getArgumentRange() { + return null; + } } - export class NumberArgument { + public static typeName() { + return 'NUMERIC'; + } - public static typeName() { - return "NUMERIC"; - } - - async validateInput(input): Promise { - return /^[0-9\.]+$/.test(input); - } + async validateInput(input): Promise { + return /^[0-9\.]+$/.test(input); + } - public async getArgumentRange() { - return null; - } + public async getArgumentRange() { + return null; + } } - - /** * @param name The name of the advancedTaskDefinition type (stored in field) */ export const advancedTaskDefinitionTypeFactory = (sqlClientPool, task, environment) => (name) => { - switch(name) { - case(EnvironmentSourceArgument.typeName()): - return new EnvironmentSourceArgument(sqlClientPool, environment); - break; - case(StringArgument.typeName()): - return new StringArgument(); - break; - case(NumberArgument.typeName()): - return new NumberArgument(); - break; - case(OtherEnvironmentSourceNamesArgument.typeName()): - return new OtherEnvironmentSourceNamesArgument(sqlClientPool, environment); - break; - default: - throw new Error(`Unable to find AdvancedTaskDefinitionType ${name}`); - break; - } -} + switch (name) { + case (EnvironmentSourceArgument.typeName()): + return new EnvironmentSourceArgument(sqlClientPool, environment); + break; + case (StringArgument.typeName()): + return new StringArgument(); + break; + case (NumberArgument.typeName()): + return new NumberArgument(); + break; + case (OtherEnvironmentSourceNamesArgument.typeName()): + return new OtherEnvironmentSourceNamesArgument(sqlClientPool, environment); + break; + default: + throw new Error(`Unable to find AdvancedTaskDefinitionType ${name}`); + break; + } +}; diff --git a/services/api/src/resources/task/models/taskRegistration.test.ts b/services/api/src/resources/task/models/taskRegistration.test.ts index 3acb5fd2bc..c45165d062 100644 --- a/services/api/src/resources/task/models/taskRegistration.test.ts +++ b/services/api/src/resources/task/models/taskRegistration.test.ts @@ -1,13 +1,12 @@ -import {TaskRegistration, newTaskRegistrationFromObject} from './taskRegistration' +import { newTaskRegistrationFromObject } from './taskRegistration'; -test("newTaskRegistrationFromObject returns a filled TaskRegistration object", () => { - let payloadData = { - type: "STANDARD", - environment: 5, - command: "command here" - } +test('newTaskRegistrationFromObject returns a filled TaskRegistration object', () => { + const payloadData = { + type: 'STANDARD', + environment: 5, + command: 'command here', + }; - let retObject = newTaskRegistrationFromObject(payloadData) - expect(retObject.type).toEqual("STANDARD") - -}) \ No newline at end of file + const retObject = newTaskRegistrationFromObject(payloadData); + expect(retObject.type).toEqual('STANDARD'); +}); diff --git a/services/api/src/resources/task/models/taskRegistration.ts b/services/api/src/resources/task/models/taskRegistration.ts index f32a9fec26..c2ea97f68e 100644 --- a/services/api/src/resources/task/models/taskRegistration.ts +++ b/services/api/src/resources/task/models/taskRegistration.ts @@ -1,26 +1,41 @@ export const newTaskRegistrationFromObject = ( - payload: Partial + payload: Partial, ) => { - let obj = new TaskRegistration(); + const obj = new TaskRegistration(); return { ...obj, ...payload }; }; export class TaskRegistration { - static TYPE_STANDARD = "COMMAND" - static TYPE_ADVANCED = "IMAGE" + static TYPE_STANDARD = 'COMMAND'; + + static TYPE_ADVANCED = 'IMAGE'; + id: number; + type: string; + name: string; + description: string; + advanced_task_definition: number; + environment: number; + project: number; + groupName: string; + command: string; + service: string; + created: string; + deleted: string; + image: string; + permission: string; } @@ -45,10 +60,9 @@ export interface AdvancedTaskDefinitionInterface { advancedTaskDefinitionArguments?: Partial; } - export const AdvancedTaskDefinitionType = { command: 'COMMAND', - image: 'IMAGE' + image: 'IMAGE', }; export interface AdvancedTaskDefinitionArguments { @@ -56,21 +70,17 @@ export interface AdvancedTaskDefinitionArguments { type?: string; range?: string; advancedTaskDefinition?: number; -}; +} export const getAdvancedTaskDefinitionType = (taskDef:AdvancedTaskDefinitionInterface) => { - if(taskDef.type.toLowerCase() == AdvancedTaskDefinitionType.command.toLowerCase()) { - return AdvancedTaskDefinitionType.command; - } - return AdvancedTaskDefinitionType.image; + if (taskDef.type.toLowerCase() === AdvancedTaskDefinitionType.command.toLowerCase()) { + return AdvancedTaskDefinitionType.command; } + return AdvancedTaskDefinitionType.image; +}; -export const isAdvancedTaskDefinitionSystemLevelTask = (taskDef:AdvancedTaskDefinitionInterface): boolean => { - return taskDef.systemWide == true; -} +export const isAdvancedTaskDefinitionSystemLevelTask = (taskDef:AdvancedTaskDefinitionInterface): boolean => taskDef.systemWide === true; -export const doesAdvancedTaskDefinitionNeedAdminRights = (taskDef:AdvancedTaskDefinitionInterface): boolean => { - return isAdvancedTaskDefinitionSystemLevelTask(taskDef) - || getAdvancedTaskDefinitionType(taskDef) == AdvancedTaskDefinitionType.image - || taskDef.groupName != undefined; -} +export const doesAdvancedTaskDefinitionNeedAdminRights = (taskDef:AdvancedTaskDefinitionInterface): boolean => isAdvancedTaskDefinitionSystemLevelTask(taskDef) + || getAdvancedTaskDefinitionType(taskDef) === AdvancedTaskDefinitionType.image + || taskDef.groupName !== undefined; diff --git a/services/api/src/resources/task/resolvers.ts b/services/api/src/resources/task/resolvers.ts index 12ce3c672e..9808016829 100644 --- a/services/api/src/resources/task/resolvers.ts +++ b/services/api/src/resources/task/resolvers.ts @@ -1,9 +1,10 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import sha1 from 'sha1'; +import { ResolverFn } from '..'; import { pubSub, createEnvironmentFilteredSubscriber, - EVENTS + EVENTS, } from '../../clients/pubSub'; import { knex, query, isPatchEmpty } from '../../util/db'; import { Sql } from './sql'; @@ -13,27 +14,28 @@ import { Helpers as environmentHelpers } from '../environment/helpers'; import { Helpers as projectHelpers } from '../project/helpers'; import { Helpers as deploymentHelpers } from '../deployment/helpers'; import { Validators as envValidators } from '../environment/validators'; -const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); -import sha1 from 'sha1'; -import { generateTaskName } from '@lagoon/commons/dist/util/lagoon'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; -import { createMiscTask } from '@lagoon/commons/dist/tasks'; -import { TaskSourceType, AuditType } from '@lagoon/commons/dist/types'; + +import { generateTaskName } from '../../commons/util/lagoon'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; +import { createMiscTask } from '../../commons/tasks'; +import { TaskSourceType, AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; import { HistoryRetentionEnforcer } from '../retentionpolicy/history'; -const accessKeyId = process.env.S3_FILES_ACCESS_KEY_ID || 'minio' -const secretAccessKey = process.env.S3_FILES_SECRET_ACCESS_KEY || 'minio123' -const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files' -const region = process.env.S3_FILES_REGION || 'us-east-1' -const s3Origin = process.env.S3_FILES_HOST || 'http://docker.for.mac.localhost:9000' +const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); + +const accessKeyId = process.env.S3_FILES_ACCESS_KEY_ID || 'minio'; +const secretAccessKey = process.env.S3_FILES_SECRET_ACCESS_KEY || 'minio123'; +const bucket = process.env.S3_FILES_BUCKET || 'lagoon-files'; +const region = process.env.S3_FILES_REGION || 'us-east-1'; +const s3Origin = process.env.S3_FILES_HOST || 'http://docker.for.mac.localhost:9000'; const config = { origin: s3Origin, - accessKeyId: accessKeyId, - secretAccessKey: secretAccessKey, - region: region, - bucket: bucket + accessKeyId, + secretAccessKey, + region, + bucket, }; const s3Client = new S3Client({ @@ -43,39 +45,40 @@ const s3Client = new S3Client({ secretAccessKey: config.secretAccessKey, }, region: config.region, - forcePathStyle: true + forcePathStyle: true, }); export const getTaskLog: ResolverFn = async ( - { remoteId, environment, id, status }, + { + remoteId, environment, id, _status, + }, _args, - { sqlClientPool } + { sqlClientPool }, ) => { if (!remoteId) { return null; } const environmentData = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(parseInt(environment)); const projectData = await projectHelpers(sqlClientPool).getProjectById( - environmentData.project + environmentData.project, ); // we need to get the safename of the environment from when it was created - const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g,'-') - var environmentName = makeSafe(environmentData.name) - var overlength = 58 - projectData.name.length; - if ( environmentName.length > overlength ) { - var hash = sha1(environmentName).substring(0,4) - environmentName = environmentName.substring(0, overlength-5) - environmentName = environmentName.concat('-' + hash) + const makeSafe = string => string.toLocaleLowerCase().replace(/[^0-9a-z-]/g, '-'); + let environmentName = makeSafe(environmentData.name); + const overlength = 58 - projectData.name.length; + if (environmentName.length > overlength) { + const hash = sha1(environmentName).substring(0, 4); + environmentName = environmentName.substring(0, overlength - 5); + environmentName = environmentName.concat(`-${hash}`); } - try { // where it should be, check `tasklogs/projectName/environmentName/taskId-remoteId.txt` - let taskLog = 'tasklogs/'+projectData.name+'/'+environmentName+'/'+id+'-'+remoteId+'.txt' + const taskLog = `tasklogs/${projectData.name}/${environmentName}/${id}-${remoteId}.txt`; const response = await s3Client.send(new GetObjectCommand({ Bucket: bucket, Key: taskLog })); const data = await response.Body.transformToString('utf-8'); @@ -84,10 +87,10 @@ export const getTaskLog: ResolverFn = async ( } return data; - } catch (e) { + } catch (_e) { // if it isn't where it should be, check the fallback location which will be `tasklogs/projectName/taskId-remoteId.txt` try { - let taskLog = 'tasklogs/'+projectData.name+'/'+id+'-'+remoteId+'.txt' + const taskLog = `tasklogs/${projectData.name}/${id}-${remoteId}.txt`; const response = await s3Client.send(new GetObjectCommand({ Bucket: bucket, Key: taskLog })); const data = await response.Body.transformToString('utf-8'); @@ -105,17 +108,17 @@ export const getTaskLog: ResolverFn = async ( export const getTasksByEnvironmentId: ResolverFn = async ( { id: eid }, - { id: filterId, taskName: taskName, limit }, - { sqlClientPool, hasPermission, adminScopes } + { id: filterId, taskName, limit }, + { sqlClientPool, hasPermission, adminScopes }, ) => { const environment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(eid); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('task', 'view', { - project: environment.project + project: environment.project, }); } @@ -136,7 +139,7 @@ export const getTasksByEnvironmentId: ResolverFn = async ( queryBuilder = queryBuilder.limit(limit); } - let rows = await query(sqlClientPool, queryBuilder.toString()) + let rows = await query(sqlClientPool, queryBuilder.toString()); rows = await Filters.filterAdminTasks(hasPermission, rows); return rows; @@ -145,7 +148,7 @@ export const getTasksByEnvironmentId: ResolverFn = async ( export const getTaskByTaskName: ResolverFn = async ( root, { taskName }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const queryString = knex('task') .where('task_name', '=', taskName) @@ -163,7 +166,7 @@ export const getTaskByTaskName: ResolverFn = async ( await hasPermission('project', 'viewAll'); } else { await hasPermission('task', 'view', { - project: R.path(['0', 'pid'], rowsPerms) + project: R.path(['0', 'pid'], rowsPerms), }); } @@ -173,7 +176,7 @@ export const getTaskByTaskName: ResolverFn = async ( export const getTaskByRemoteId: ResolverFn = async ( root, { id }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const queryString = knex('task') .where('remote_id', '=', id) @@ -191,7 +194,7 @@ export const getTaskByRemoteId: ResolverFn = async ( await hasPermission('project', 'viewAll'); } else { await hasPermission('task', 'view', { - project: R.path(['0', 'pid'], rowsPerms) + project: R.path(['0', 'pid'], rowsPerms), }); } @@ -201,7 +204,7 @@ export const getTaskByRemoteId: ResolverFn = async ( export const getTaskById: ResolverFn = async ( root, { id }, - { sqlClientPool, hasPermission } + { sqlClientPool, hasPermission }, ) => { const queryString = knex('task') .where('id', '=', id) @@ -219,7 +222,7 @@ export const getTaskById: ResolverFn = async ( await hasPermission('project', 'viewAll'); } else { await hasPermission('task', 'view', { - project: R.path(['0', 'pid'], rowsPerms) + project: R.path(['0', 'pid'], rowsPerms), }); } @@ -246,33 +249,35 @@ export const addTask: ResolverFn = async ( execute: executeRequest, sourceType, sourceUser, - } + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } ) => { await envValidators(sqlClientPool).environmentExists(environment); const envPerm = await environmentHelpers(sqlClientPool).getEnvironmentById( - environment + environment, ); const project = await projectHelpers(sqlClientPool).getProjectById(envPerm.project); await hasPermission('task', `add:${envPerm.environmentType}`, { - project: envPerm.project + project: envPerm.project, }); let execute; try { await hasPermission('task', 'addNoExec', { - project: envPerm.project + project: envPerm.project, }); execute = executeRequest; - } catch (err) { + } catch (_err) { execute = true; } - let taskName = generateTaskName() + const taskName = generateTaskName(); - sourceUser ??= await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) - sourceType ??= TaskSourceType.API + sourceUser ??= await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); + sourceType ??= TaskSourceType.API; const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ id, @@ -291,7 +296,7 @@ export const addTask: ResolverFn = async ( adminOnlyView, execute, sourceUser, - sourceType + sourceType, }); const auditLog: AuditLog = { @@ -330,7 +335,7 @@ export const addTask: ResolverFn = async ( sourceType, }, ...auditLog, - } + }, }); return taskData; @@ -339,18 +344,20 @@ export const addTask: ResolverFn = async ( export const deleteTask: ResolverFn = async ( root, { input: { id } }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, + }, ) => { const rows = await query(sqlClientPool, Sql.selectPermsForTask(id)); await hasPermission('task', 'delete', { - project: R.path(['0', 'pid'], rows) + project: R.path(['0', 'pid'], rows), }); - const task = await Helpers(sqlClientPool, hasPermission, adminScopes).getTaskByTaskInput({id: id}) + const task = await Helpers(sqlClientPool, hasPermission, adminScopes).getTaskByTaskInput({ id }); if (!task) { throw new Error( - `Invalid task input` + 'Invalid task input', ); } @@ -360,7 +367,7 @@ export const deleteTask: ResolverFn = async ( await query(sqlClientPool, Sql.deleteTask(id)); // pass the task to the HistoryRetentionEnforcer - await HistoryRetentionEnforcer().cleanupTask(projectData, environmentData, task) + await HistoryRetentionEnforcer().cleanupTask(projectData, environmentData, task); const auditLog: AuditLog = { resource: { @@ -376,10 +383,10 @@ export const deleteTask: ResolverFn = async ( event: 'api:deleteTask', payload: { input: { - id + id, }, ...auditLog, - } + }, }); return 'success'; @@ -390,11 +397,12 @@ export const cancelTask: ResolverFn = async ( { input: { task: taskInput, - } + }, + }, + { + sqlClientPool, hasPermission, userActivityLogger, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, adminScopes } ) => { - const task = await Helpers(sqlClientPool, hasPermission, adminScopes).getTaskByTaskInput(taskInput); if (!task) { return null; @@ -404,10 +412,9 @@ export const cancelTask: ResolverFn = async ( const project = await projectHelpers(sqlClientPool).getProjectById(environment.project); await hasPermission('task', `cancel:${environment.environmentType}`, { - project: project.id + project: project.id, }); - const data = { task: { id: task.id.toString(), @@ -416,7 +423,7 @@ export const cancelTask: ResolverFn = async ( service: task.service, }, environment, - project + project, }; const auditLog: AuditLog = { @@ -443,8 +450,8 @@ export const cancelTask: ResolverFn = async ( taskInput, data: data.task, ...auditLog, - } - } + }, + }, ); try { @@ -457,7 +464,7 @@ export const cancelTask: ResolverFn = async ( '', 'api:cancelTask', { taskId: task.id }, - `Task not cancelled, reason: ${error}` + `Task not cancelled, reason: ${error}`, ); return `Error: ${error.message}`; } @@ -479,31 +486,31 @@ export const updateTask: ResolverFn = async ( service, command, deployTokenInjection, - remoteId - } - } + remoteId, + }, + }, }, - { sqlClientPool, hasPermission, userActivityLogger } + { sqlClientPool, hasPermission, userActivityLogger }, ) => { // Check access to modify task as it currently stands const curPerms = await query(sqlClientPool, Sql.selectPermsForTask(id)); await hasPermission('task', 'update', { - project: R.path(['0', 'pid'], curPerms) + project: R.path(['0', 'pid'], curPerms), }); const task = await query(sqlClientPool, Sql.selectTask(id)); const env = await environmentHelpers(sqlClientPool).getEnvironmentById( - task[0].environment + task[0].environment, ); const project = await projectHelpers(sqlClientPool).getProjectById(R.path(['0', 'pid'], curPerms)); if (environment) { // Check access to modify task as it will be updated const envPerm = await environmentHelpers(sqlClientPool).getEnvironmentById( - environment + environment, ); await hasPermission('task', 'update', { - project: envPerm.project + project: envPerm.project, }); } @@ -525,9 +532,9 @@ export const updateTask: ResolverFn = async ( service, command, deployTokenInjection, - remoteId - } - }) + remoteId, + }, + }), ); const rows = await query(sqlClientPool, Sql.selectTask(id)); @@ -564,10 +571,10 @@ export const updateTask: ResolverFn = async ( service, command, deployTokenInjection, - remoteId + remoteId, }, ...auditLog, - } + }, }); return taskData; @@ -576,28 +583,30 @@ export const updateTask: ResolverFn = async ( export const taskDrushArchiveDump: ResolverFn = async ( root, { environment: environmentId }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, + }, ) => { await envValidators(sqlClientPool).environmentExists(environmentId); const envPerm = await environmentHelpers(sqlClientPool).getEnvironmentById( - environmentId + environmentId, ); await hasPermission('task', `drushArchiveDump:${envPerm.environmentType}`, { - project: envPerm.project + project: envPerm.project, }); await envValidators(sqlClientPool).environmentHasService( environmentId, - 'cli' + 'cli', ); const project = await projectHelpers(sqlClientPool).getProjectById(envPerm.project); - const command = String.raw` + const command = `${String.raw` cat << 'EOF' | sh curl -sSL https://github.com/itchyny/gojq/releases/download/v0.12.16/gojq_v0.12.16_linux_amd64.tar.gz | tar -xzC /tmp --strip-components=1 gojq_v0.12.16_linux_amd64/gojq chmod +x /tmp/gojq FILENAME="$LAGOON_PROJECT-$LAGOON_GIT_SAFE_BRANCH-$(date --iso-8601=seconds).tar" -file="/tmp/`+"${FILENAME}"+`" +file="/tmp/`}\${FILENAME}` + `" if drush ard --destination=$file then echo "drush ard complete" @@ -616,8 +625,8 @@ script='query { script="$(echo $script | sed 's/"/\\\\"/g' | sed 's/\\\\n/\\\\\\\\n/g' | awk -F'\\n' '{if(NR == 1) {printf $0} else {printf "\\\\n"$0}}')" curl -ksS -H 'Content-Type: application/json' \\ - -H "Authorization: bearer $(ssh -p `+"${LAGOON_CONFIG_TOKEN_PORT:-$TASK_SSH_PORT}"+` -t lagoon@`+"${LAGOON_CONFIG_TOKEN_HOST:-$TASK_SSH_HOST}"+` token)" \\ - -X POST -d "{ \\"query\\": \\"$script\\"}" "`+"${LAGOON_CONFIG_API_HOST:-$TASK_API_HOST}"+`/graphql" | /tmp/gojq -r '.data.getTaskFileUploadForm as $signedurl | + -H "Authorization: bearer $(ssh -p ` + '${LAGOON_CONFIG_TOKEN_PORT:-$TASK_SSH_PORT}' + ' -t lagoon@' + '${LAGOON_CONFIG_TOKEN_HOST:-$TASK_SSH_HOST}' + ` token)" \\ + -X POST -d "{ \\"query\\": \\"$script\\"}" "` + '${LAGOON_CONFIG_API_HOST:-$TASK_API_HOST}' + `/graphql" | /tmp/gojq -r '.data.getTaskFileUploadForm as $signedurl | "curl -X POST \\\"" + $signedurl.postUrl + "\\\" " + ( $signedurl.formFields @@ -630,7 +639,7 @@ rm -rf $file EOF `; - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ name: 'Drush archive-dump', taskName: generateTaskName(), @@ -642,7 +651,7 @@ EOF adminOnlyView: false, execute: true, sourceType: TaskSourceType.API, - sourceUser: sourceUser, + sourceUser, }); const auditLog: AuditLog = { @@ -654,7 +663,7 @@ EOF linkedResource: { id: taskData.id.toString(), type: AuditType.TASK, - details: "Drush Archive Dump task", + details: 'Drush Archive Dump task', }, }; if (project.organization) { @@ -666,7 +675,7 @@ EOF payload: { environment: environmentId, ...auditLog, - } + }, }); return taskData; @@ -675,28 +684,30 @@ EOF export const taskDrushSqlDump: ResolverFn = async ( root, { environment: environmentId }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, + }, ) => { await envValidators(sqlClientPool).environmentExists(environmentId); const envPerm = await environmentHelpers(sqlClientPool).getEnvironmentById( - environmentId + environmentId, ); await hasPermission('task', `drushSqlDump:${envPerm.environmentType}`, { - project: envPerm.project + project: envPerm.project, }); await envValidators(sqlClientPool).environmentHasService( environmentId, - 'cli' + 'cli', ); const project = await projectHelpers(sqlClientPool).getProjectById(envPerm.project); - const command = String.raw` + const command = `${String.raw` cat << 'EOF' | sh curl -sSL https://github.com/itchyny/gojq/releases/download/v0.12.16/gojq_v0.12.16_linux_amd64.tar.gz | tar -xzC /tmp --strip-components=1 gojq_v0.12.16_linux_amd64/gojq chmod +x /tmp/gojq FILENAME="$LAGOON_PROJECT-$LAGOON_GIT_SAFE_BRANCH-$(date --iso-8601=seconds).sql" -file="/tmp/`+"${FILENAME}"+`" +file="/tmp/`}\${FILENAME}` + `" DRUSH_MAJOR_VERSION=$(drush status --fields=drush-version | awk '{ print $4 }' | grep -oE '^s*[0-9]+') if [[ $DRUSH_MAJOR_VERSION -ge 9 ]] then @@ -726,8 +737,8 @@ script='query { script="$(echo $script | sed 's/"/\\\\"/g' | sed 's/\\\\n/\\\\\\\\n/g' | awk -F'\\n' '{if(NR == 1) {printf $0} else {printf "\\\\n"$0}}')" curl -ksS -H 'Content-Type: application/json' \\ - -H "Authorization: bearer $(ssh -p `+"${LAGOON_CONFIG_TOKEN_PORT:-$TASK_SSH_PORT}"+` -t lagoon@`+"${LAGOON_CONFIG_TOKEN_HOST:-$TASK_SSH_HOST}"+` token)" \\ - -X POST -d "{ \\"query\\": \\"$script\\"}" "`+"${LAGOON_CONFIG_API_HOST:-$TASK_API_HOST}"+`/graphql" | /tmp/gojq -r '.data.getTaskFileUploadForm as $signedurl | + -H "Authorization: bearer $(ssh -p ` + '${LAGOON_CONFIG_TOKEN_PORT:-$TASK_SSH_PORT}' + ' -t lagoon@' + '${LAGOON_CONFIG_TOKEN_HOST:-$TASK_SSH_HOST}' + ` token)" \\ + -X POST -d "{ \\"query\\": \\"$script\\"}" "` + '${LAGOON_CONFIG_API_HOST:-$TASK_API_HOST}' + `/graphql" | /tmp/gojq -r '.data.getTaskFileUploadForm as $signedurl | "curl -X POST \\\"" + $signedurl.postUrl + "\\\" " + ( $signedurl.formFields @@ -740,7 +751,7 @@ rm -rf $file EOF `; - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ name: 'Drush sql-dump', taskName: generateTaskName(), @@ -752,7 +763,7 @@ EOF adminOnlyView: false, execute: true, sourceType: TaskSourceType.API, - sourceUser: sourceUser, + sourceUser, }); const auditLog: AuditLog = { @@ -764,7 +775,7 @@ EOF linkedResource: { id: taskData.id.toString(), type: AuditType.TASK, - details: "Drush SQL Dump task", + details: 'Drush SQL Dump task', }, }; if (project.organization) { @@ -776,7 +787,7 @@ EOF payload: { environment: environmentId, ...auditLog, - } + }, }); return taskData; @@ -785,33 +796,36 @@ EOF export const taskDrushCacheClear: ResolverFn = async ( root, { environment: environmentId }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, + }, ) => { await envValidators(sqlClientPool).environmentExists(environmentId); const envPerm = await environmentHelpers(sqlClientPool).getEnvironmentById( - environmentId + environmentId, ); await hasPermission('task', `drushCacheClear:${envPerm.environmentType}`, { - project: envPerm.project + project: envPerm.project, }); await envValidators(sqlClientPool).environmentHasService( environmentId, - 'cli' + 'cli', ); const project = await projectHelpers(sqlClientPool).getProjectById(envPerm.project); - const command = - 'drupal_version=$(drush status | grep -i "drupal version" | awk \'{print $NF}\') && \ - if [ ${drupal_version%.*} == "7" ]; then \ - if drush cc all; then echo "drush cc all complete"; else exit $?; fi; \ - elif [ ${drupal_version%.*.*} -ge "8" ] ; then \ - if drush cr -y; then echo "drush cache:rebuild complete"; else exit $?; fi; \ - else \ - echo "could not clear cache for found Drupal Version ${drupal_version}"; \ - exit 1; \ - fi'; - - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const command = [ + 'drupal_version=$(drush status | grep -i "drupal version" | awk \'{print $NF}\')', + 'if [ ${drupal_version%.*} == "7" ]; then', + ' if drush cc all; then echo "drush cc all complete"; else exit $?; fi;', + 'elif [ ${drupal_version%.*.*} -ge "8" ] ; then', + ' if drush cr -y; then echo "drush cache:rebuild complete"; else exit $?; fi;', + 'else', + ' echo "could not clear cache for found Drupal Version ${drupal_version}";', + ' exit 1;', + 'fi', + ].join(' && \\\n '); + + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ name: 'Drush cache-clear', taskName: generateTaskName(), @@ -823,7 +837,7 @@ export const taskDrushCacheClear: ResolverFn = async ( adminOnlyView: false, execute: true, sourceType: TaskSourceType.API, - sourceUser: sourceUser, + sourceUser, }); const auditLog: AuditLog = { @@ -835,7 +849,7 @@ export const taskDrushCacheClear: ResolverFn = async ( linkedResource: { id: taskData.id.toString(), type: AuditType.TASK, - details: "Drush cache clear task", + details: 'Drush cache clear task', }, }; if (project.organization) { @@ -847,7 +861,7 @@ export const taskDrushCacheClear: ResolverFn = async ( payload: { environment: environmentId, ...auditLog, - } + }, }); return taskData; @@ -856,34 +870,36 @@ export const taskDrushCacheClear: ResolverFn = async ( export const taskDrushCron: ResolverFn = async ( root, { environment: environmentId }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, + }, ) => { await envValidators(sqlClientPool).environmentExists(environmentId); const envPerm = await environmentHelpers(sqlClientPool).getEnvironmentById( - environmentId + environmentId, ); await hasPermission('task', `drushCron:${envPerm.environmentType}`, { - project: envPerm.project + project: envPerm.project, }); await envValidators(sqlClientPool).environmentHasService( environmentId, - 'cli' + 'cli', ); const project = await projectHelpers(sqlClientPool).getProjectById(envPerm.project); - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ name: 'Drush cron', taskName: generateTaskName(), environment: environmentId, service: 'cli', - command: `drush cron`, + command: 'drush cron', deployTokenInjection: false, projectKeyInjection: false, adminOnlyView: false, execute: true, sourceType: TaskSourceType.API, - sourceUser: sourceUser, + sourceUser, }); const auditLog: AuditLog = { @@ -895,7 +911,7 @@ export const taskDrushCron: ResolverFn = async ( linkedResource: { id: taskData.id.toString(), type: AuditType.TASK, - details: "Drush cron task", + details: 'Drush cron task', }, }; if (project.organization) { @@ -907,7 +923,7 @@ export const taskDrushCron: ResolverFn = async ( payload: { environment: environmentId, ...auditLog, - } + }, }); return taskData; @@ -917,64 +933,65 @@ export const taskDrushSqlSync: ResolverFn = async ( root, { sourceEnvironment: sourceEnvironmentId, - destinationEnvironment: destinationEnvironmentId + destinationEnvironment: destinationEnvironmentId, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } ) => { await envValidators(sqlClientPool).environmentExists(sourceEnvironmentId); await envValidators(sqlClientPool).environmentExists( - destinationEnvironmentId + destinationEnvironmentId, ); const sourceEnvironment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(sourceEnvironmentId); const destinationEnvironment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(destinationEnvironmentId); await hasPermission( 'task', `drushSqlSync:source:${sourceEnvironment.environmentType}`, { - project: sourceEnvironment.project - } + project: sourceEnvironment.project, + }, ); await hasPermission( 'task', `drushSqlSync:destination:${destinationEnvironment.environmentType}`, { - project: destinationEnvironment.project - } + project: destinationEnvironment.project, + }, ); await envValidators(sqlClientPool).environmentsHaveSameProject([ sourceEnvironmentId, - destinationEnvironmentId + destinationEnvironmentId, ]); await envValidators(sqlClientPool).environmentHasService( sourceEnvironmentId, - 'cli' + 'cli', ); const project = await projectHelpers(sqlClientPool).getProjectById(sourceEnvironment.project); - const command = - `LAGOON_ALIAS_PREFIX="" && \ + const command = `LAGOON_ALIAS_PREFIX="" && \ if [[ ! "" = "$(drush | grep 'lagoon:aliases')" ]]; then LAGOON_ALIAS_PREFIX="lagoon.\${LAGOON_PROJECT}-"; fi && \ drush -y sql-sync @\${LAGOON_ALIAS_PREFIX}${sourceEnvironment.name} @self`; - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ name: `Sync DB ${sourceEnvironment.name} -> ${destinationEnvironment.name}`, taskName: generateTaskName(), environment: destinationEnvironmentId, service: 'cli', - command: command, + command, deployTokenInjection: false, projectKeyInjection: false, adminOnlyView: false, execute: true, sourceType: TaskSourceType.API, - sourceUser: sourceUser, + sourceUser, }); const auditLog: AuditLog = { @@ -999,7 +1016,7 @@ export const taskDrushSqlSync: ResolverFn = async ( sourceEnvironment: sourceEnvironmentId, destinationEnvironment: destinationEnvironmentId, ...auditLog, - } + }, }); return taskData; @@ -1009,64 +1026,65 @@ export const taskDrushRsyncFiles: ResolverFn = async ( root, { sourceEnvironment: sourceEnvironmentId, - destinationEnvironment: destinationEnvironmentId + destinationEnvironment: destinationEnvironmentId, + }, + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } ) => { await envValidators(sqlClientPool).environmentExists(sourceEnvironmentId); await envValidators(sqlClientPool).environmentExists( - destinationEnvironmentId + destinationEnvironmentId, ); const sourceEnvironment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(sourceEnvironmentId); const destinationEnvironment = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(destinationEnvironmentId); await hasPermission( 'task', `drushRsync:source:${sourceEnvironment.environmentType}`, { - project: sourceEnvironment.project - } + project: sourceEnvironment.project, + }, ); await hasPermission( 'task', `drushRsync:destination:${destinationEnvironment.environmentType}`, { - project: destinationEnvironment.project - } + project: destinationEnvironment.project, + }, ); await envValidators(sqlClientPool).environmentsHaveSameProject([ sourceEnvironmentId, - destinationEnvironmentId + destinationEnvironmentId, ]); await envValidators(sqlClientPool).environmentHasService( sourceEnvironmentId, - 'cli' + 'cli', ); const project = await projectHelpers(sqlClientPool).getProjectById(sourceEnvironment.project); - const command = - `LAGOON_ALIAS_PREFIX="" && \ + const command = `LAGOON_ALIAS_PREFIX="" && \ if [[ ! "" = "$(drush | grep 'lagoon:aliases')" ]]; then LAGOON_ALIAS_PREFIX="lagoon.\${LAGOON_PROJECT}-"; fi && \ drush -y rsync @\${LAGOON_ALIAS_PREFIX}${sourceEnvironment.name}:%files @self:%files -- --omit-dir-times --no-perms --no-group --no-owner --chmod=ugo=rwX`; - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ name: `Sync files ${sourceEnvironment.name} -> ${destinationEnvironment.name}`, taskName: generateTaskName(), environment: destinationEnvironmentId, service: 'cli', - command: command, + command, deployTokenInjection: false, projectKeyInjection: false, adminOnlyView: false, execute: true, sourceType: TaskSourceType.API, - sourceUser: sourceUser, + sourceUser, }); const auditLog: AuditLog = { @@ -1091,7 +1109,7 @@ export const taskDrushRsyncFiles: ResolverFn = async ( sourceEnvironment: sourceEnvironmentId, destinationEnvironment: destinationEnvironmentId, ...auditLog, - } + }, }); return taskData; @@ -1100,34 +1118,36 @@ export const taskDrushRsyncFiles: ResolverFn = async ( export const taskDrushUserLogin: ResolverFn = async ( root, { environment: environmentId }, - { sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes } + { + sqlClientPool, hasPermission, userActivityLogger, keycloakGrant, legacyGrant, adminScopes, + }, ) => { await envValidators(sqlClientPool).environmentExists(environmentId); const envPerm = await environmentHelpers(sqlClientPool).getEnvironmentById( - environmentId + environmentId, ); await hasPermission('task', `drushUserLogin:${envPerm.environmentType}`, { - project: envPerm.project + project: envPerm.project, }); await envValidators(sqlClientPool).environmentHasService( environmentId, - 'cli' + 'cli', ); const project = await projectHelpers(sqlClientPool).getProjectById(envPerm.project); - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ name: 'Drush uli', taskName: generateTaskName(), environment: environmentId, service: 'cli', - command: `drush uli`, + command: 'drush uli', deployTokenInjection: false, projectKeyInjection: false, adminOnlyView: false, execute: true, sourceType: TaskSourceType.API, - sourceUser: sourceUser, + sourceUser, }); const auditLog: AuditLog = { @@ -1139,7 +1159,7 @@ export const taskDrushUserLogin: ResolverFn = async ( linkedResource: { id: taskData.id.toString(), type: AuditType.TASK, - details: `Drush user login task`, + details: 'Drush user login task', }, }; if (project.organization) { @@ -1151,12 +1171,12 @@ export const taskDrushUserLogin: ResolverFn = async ( payload: { environment: environmentId, ...auditLog, - } + }, }); return taskData; }; export const taskSubscriber = createEnvironmentFilteredSubscriber([ - EVENTS.TASK + EVENTS.TASK, ]); diff --git a/services/api/src/resources/task/sql.ts b/services/api/src/resources/task/sql.ts index 313b464121..83da83c87c 100644 --- a/services/api/src/resources/task/sql.ts +++ b/services/api/src/resources/task/sql.ts @@ -1,16 +1,14 @@ -import { TaskSourceType } from '@lagoon/commons/dist/types'; +import { TaskSourceType } from '../../commons/types'; import { knex } from '../../util/db'; export const Sql = { - selectTask: (id: number) => - knex('task') - .where('task.id', '=', id) - .toString(), - selectTaskByNameAndEnvironment: (name: string, environmentId: number) => - knex('task') - .where('task_name', '=', name) - .andWhere('environment', '=', environmentId) - .toString(), + selectTask: (id: number) => knex('task') + .where('task.id', '=', id) + .toString(), + selectTaskByNameAndEnvironment: (name: string, environmentId: number) => knex('task') + .where('task_name', '=', name) + .andWhere('environment', '=', environmentId) + .toString(), insertTask: ({ id, name, @@ -51,46 +49,44 @@ export const Sql = { advanced_payload?: string; sourceUser?: string; sourceType?: TaskSourceType; - }) => - knex('task') - .insert({ - id, - name, - taskName, - status, - created, - started, - completed, - environment, - service, - command, - deployTokenInjection, - projectKeyInjection, - adminOnlyView, - remoteId, - type: type ?? null, - advanced_image: advanced_image ?? null, - advanced_payload: advanced_payload ?? null, - sourceUser: sourceUser ?? null, - sourceType: sourceType ?? null, - }) - .toString(), - deleteTask: (id: number) => - knex('task') - .where('id', id) - .del() - .toString(), - updateTask: ({ id, patch }: { id: number; patch: { [key: string]: any } }) => - knex('task') - .where('id', id) - .update(patch) - .toString(), - selectPermsForTask: (id: number) => - knex('task') - .select({ pid: 'environment.project', adminOnlyView: 'task.admin_only_view', deployTokenInjection: 'task.deploy_token_injection', projectKeyInjection: 'task.project_key_injection' }) - .join('environment', 'task.environment', '=', 'environment.id') - .where('task.id', id) - .toString(), + }) => knex('task') + .insert({ + id, + name, + taskName, + status, + created, + started, + completed, + environment, + service, + command, + deployTokenInjection, + projectKeyInjection, + adminOnlyView, + remoteId, + type: type ?? null, + advanced_image: advanced_image ?? null, + advanced_payload: advanced_payload ?? null, + sourceUser: sourceUser ?? null, + sourceType: sourceType ?? null, + }) + .toString(), + deleteTask: (id: number) => knex('task') + .where('id', id) + .del() + .toString(), + updateTask: ({ id, patch }: { id: number; patch: { [key: string]: any } }) => knex('task') + .where('id', id) + .update(patch) + .toString(), + selectPermsForTask: (id: number) => knex('task') + .select({ + pid: 'environment.project', adminOnlyView: 'task.admin_only_view', deployTokenInjection: 'task.deploy_token_injection', projectKeyInjection: 'task.project_key_injection', + }) + .join('environment', 'task.environment', '=', 'environment.id') + .where('task.id', id) + .toString(), insertAdvancedTaskDefinition: ({ id, name, @@ -109,7 +105,7 @@ export const Sql = { project_key_injection, admin_only_view, system_wide, - }: { + }: { id: number, name: string, description: string, @@ -127,37 +123,36 @@ export const Sql = { project_key_injection: boolean, admin_only_view: boolean, system_wide: boolean, - }) => - knex('advanced_task_definition') - .insert({ - id, - name, - description, - image, - command, - created, - type, - service, - project, - group_name, - environment, - permission, - confirmation_text, - deploy_token_injection, - project_key_injection, - admin_only_view, - system_wide, - }) - .toString(), - insertAdvancedTaskDefinitionArgument: ({ + }) => knex('advanced_task_definition') + .insert({ id, - advanced_task_definition, name, + description, + image, + command, + created, type, - displayName, - defaultValue, - optional, - }: { + service, + project, + group_name, + environment, + permission, + confirmation_text, + deploy_token_injection, + project_key_injection, + admin_only_view, + system_wide, + }) + .toString(), + insertAdvancedTaskDefinitionArgument: ({ + id, + advanced_task_definition, + name, + type, + displayName, + defaultValue, + optional, + }: { id: number, advanced_task_definition: number, name: string, @@ -165,201 +160,176 @@ export const Sql = { defaultValue: string, optional: boolean, displayName: string, - }) => - knex('advanced_task_definition_argument') - .insert({ - id, - advanced_task_definition, - name, - type, - defaultValue, - optional, - display_name: displayName - }) - .toString(), - updateAdvancedTaskDefinition: ({ id, patch }: { id: number; patch: { [key: string]: any } }) => - knex('advanced_task_definition') - .where('id', id) - .update(patch) - .toString(), - selectAdvancedTaskDefinitionEnvironmentLinkById: (id: number) => - knex('task_registration') - .where('task_registration.id', '=', id) - .toString(), - selectTaskRegistrationById: (id: number) => - knex('task_registration') - .where('task_registration.id', '=', id) - .toString(), - selectTaskRegistrationsByEnvironmentId:(id: number) => - knex('advanced_task_definition') - // .select('advanced_task_definition.*', 'task_registration.id') - .select(knex.raw(`advanced_task_definition.*, task_registration.id, advanced_task_definition.admin_only_view XOR 1 as "advanced_task_definition.show_ui", advanced_task_definition.deploy_token_injection as "advanced_task_definition.admin_task"`)) //use admin_only_view as show_ui for backwards compatability - .join('task_registration', 'task_registration.advanced_task_definition', '=', 'advanced_task_definition.id') - .where('task_registration.environment', '=', id) - .toString(), - selectTaskRegistrationByEnvironmentIdAndAdvancedTaskId: (environmentId: number, task: number) => - knex('task_registration') - .where('task_registration.environment', '=', environmentId) - .where('task_registration.advanced_task_definition', '=', task) - .toString(), - selectAdvancedTaskDefinition:(id: number) => - knex('advanced_task_definition') - .select(knex.raw(`*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"`)) //use admin_only_view as show_ui for backwards compatability - .where('advanced_task_definition.id', '=', id) - .toString(), - selectAdvancedTaskDefinitionArguments:(id: number) => - knex('advanced_task_definition_argument') - .where('advanced_task_definition_argument.advanced_task_definition', '=', id) - .toString(), - selectAdvancedTaskDefinitionArgumentById:(id: number) => - knex('advanced_task_definition_argument') - .where('advanced_task_definition_argument.id', '=', id) - .toString(), - deleteAdvancedTaskDefinitionArgumentByTaskDef:(advanced_task_definition: number) => - knex('advanced_task_definition_argument') - .where('advanced_task_definition_argument.advanced_task_definition', '=', advanced_task_definition) - .del() - .toString(), - selectAdvancedTaskDefinitionByName:(name: string) => - knex('advanced_task_definition') - .select(knex.raw(`*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"`)) //use admin_only_view as show_ui for backwards compatability - .where('advanced_task_definition.name', '=', name) - .toString(), - selectAdvancedTaskDefinitionByNameProjectEnvironmentAndGroup:(name: string, project: number, environment: number, group: string, systemWide: boolean = false) => { - let query = knex('advanced_task_definition') - .where('advanced_task_definition.name', '=', name); - if(project) { - query = query.where('advanced_task_definition.project', '=', project) - } - if(environment) { - query = query.where('advanced_task_definition.environment', '=', environment) - } - if(group) { - query = query.where('advanced_task_definition.group_name', '=', group) - } - if(systemWide == true) { - query = query.where('advanced_task_definition.system_wide', '=', "1") - } - return query.toString() - }, - selectAdvancedTaskDefinitions:() => - knex('advanced_task_definition') - .select(knex.raw(`*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"`)) //use admin_only_view as show_ui for backwards compatability - .toString(), - selectAdvancedTaskDefinitionsForEnvironment:(id: number) => - knex('advanced_task_definition') - .select(knex.raw(`*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"`)) //use admin_only_view as show_ui for backwards compatability + }) => knex('advanced_task_definition_argument') + .insert({ + id, + advanced_task_definition, + name, + type, + defaultValue, + optional, + display_name: displayName, + }) + .toString(), + updateAdvancedTaskDefinition: ({ id, patch }: { id: number; patch: { [key: string]: any } }) => knex('advanced_task_definition') + .where('id', id) + .update(patch) + .toString(), + selectAdvancedTaskDefinitionEnvironmentLinkById: (id: number) => knex('task_registration') + .where('task_registration.id', '=', id) + .toString(), + selectTaskRegistrationById: (id: number) => knex('task_registration') + .where('task_registration.id', '=', id) + .toString(), + selectTaskRegistrationsByEnvironmentId: (id: number) => knex('advanced_task_definition') + // .select('advanced_task_definition.*', 'task_registration.id') + .select(knex.raw('advanced_task_definition.*, task_registration.id, advanced_task_definition.admin_only_view XOR 1 as "advanced_task_definition.show_ui", advanced_task_definition.deploy_token_injection as "advanced_task_definition.admin_task"')) // use admin_only_view as show_ui for backwards compatability + .join('task_registration', 'task_registration.advanced_task_definition', '=', 'advanced_task_definition.id') + .where('task_registration.environment', '=', id) + .toString(), + selectTaskRegistrationByEnvironmentIdAndAdvancedTaskId: (environmentId: number, task: number) => knex('task_registration') + .where('task_registration.environment', '=', environmentId) + .where('task_registration.advanced_task_definition', '=', task) + .toString(), + selectAdvancedTaskDefinition: (id: number) => knex('advanced_task_definition') + .select(knex.raw('*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"')) // use admin_only_view as show_ui for backwards compatability + .where('advanced_task_definition.id', '=', id) + .toString(), + selectAdvancedTaskDefinitionArguments: (id: number) => knex('advanced_task_definition_argument') + .where('advanced_task_definition_argument.advanced_task_definition', '=', id) + .toString(), + selectAdvancedTaskDefinitionArgumentById: (id: number) => knex('advanced_task_definition_argument') + .where('advanced_task_definition_argument.id', '=', id) + .toString(), + deleteAdvancedTaskDefinitionArgumentByTaskDef: (advanced_task_definition: number) => knex('advanced_task_definition_argument') + .where('advanced_task_definition_argument.advanced_task_definition', '=', advanced_task_definition) + .del() + .toString(), + selectAdvancedTaskDefinitionByName: (name: string) => knex('advanced_task_definition') + .select(knex.raw('*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"')) // use admin_only_view as show_ui for backwards compatability + .where('advanced_task_definition.name', '=', name) + .toString(), + selectAdvancedTaskDefinitionByNameProjectEnvironmentAndGroup: (name: string, project: number, environment: number, group: string, systemWide: boolean = false) => { + let query = knex('advanced_task_definition') + .where('advanced_task_definition.name', '=', name); + if (project) { + query = query.where('advanced_task_definition.project', '=', project); + } + if (environment) { + query = query.where('advanced_task_definition.environment', '=', environment); + } + if (group) { + query = query.where('advanced_task_definition.group_name', '=', group); + } + if (systemWide === true) { + query = query.where('advanced_task_definition.system_wide', '=', '1'); + } + return query.toString(); + }, + selectAdvancedTaskDefinitions: () => knex('advanced_task_definition') + .select(knex.raw('*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"')) // use admin_only_view as show_ui for backwards compatability + .toString(), + selectAdvancedTaskDefinitionsForEnvironment: (id: number) => knex('advanced_task_definition') + .select(knex.raw('*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"')) // use admin_only_view as show_ui for backwards compatability .where('environment', '=', id) .toString(), - selectAdvancedTaskDefinitionsForProject:(id: number) => - knex('advanced_task_definition') - .select(knex.raw(`*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"`)) //use admin_only_view as show_ui for backwards compatability + selectAdvancedTaskDefinitionsForProject: (id: number) => knex('advanced_task_definition') + .select(knex.raw('*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"')) // use admin_only_view as show_ui for backwards compatability .where('project', '=', id) .toString(), - selectAdvancedTaskDefinitionsForGroups:(groups) => - knex('advanced_task_definition') - .select(knex.raw(`*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"`)) //use admin_only_view as show_ui for backwards compatability + selectAdvancedTaskDefinitionsForGroups: (groups) => knex('advanced_task_definition') + .select(knex.raw('*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"')) // use admin_only_view as show_ui for backwards compatability .where('group_name', 'in', groups) .toString(), - selectAdvancedTaskDefinitionsForSystem:() => - knex('advanced_task_definition') - .select(knex.raw(`*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"`)) //use admin_only_view as show_ui for backwards compatability - .where('environment','is', null) - .andWhere('project','is', null) - .andWhere('group_name','is', null) - .andWhere('system_wide','=','1') + selectAdvancedTaskDefinitionsForSystem: () => knex('advanced_task_definition') + .select(knex.raw('*, advanced_task_definition.admin_only_view XOR 1 as "show_ui", advanced_task_definition.deploy_token_injection as "admin_task"')) // use admin_only_view as show_ui for backwards compatability + .where('environment', 'is', null) + .andWhere('project', 'is', null) + .andWhere('group_name', 'is', null) + .andWhere('system_wide', '=', '1') .toString(), - deleteAdvancedTaskDefinition:(id: number) => - knex('advanced_task_definition') + deleteAdvancedTaskDefinition: (id: number) => knex('advanced_task_definition') .where('id', id) .del() .toString(), - deleteAdvancedTaskDefinitionArgumentsForTask:(taskId: number) => knex('advanced_task_definition_argument') + deleteAdvancedTaskDefinitionArgumentsForTask: (taskId: number) => knex('advanced_task_definition_argument') .where('advanced_task_definition', taskId) .del() .toString(), // this selects all tasks for the environment and returns everything outside of the requested retain value - selectTaskHistoryRetention: (environment: number, retain: number) => - knex('task') - .select('id','name','remote_id') - .where('environment', environment) - .where('admin_only_view', 0) - .whereNotIn('id', function() { - this.select('id') - .from(function() { - this.select('id') - .from('task') - .where('environment', environment) - .where('admin_only_view', 0) - .orderBy('id','desc') - .limit(retain) - .as('t') - }) - }) - .toString(), + selectTaskHistoryRetention: (environment: number, retain: number) => knex('task') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where('admin_only_view', 0) + .whereNotIn('id', function () { + this.select('id') + .from(function () { + this.select('id') + .from('task') + .where('environment', environment) + .where('admin_only_view', 0) + .orderBy('id', 'desc') + .limit(retain) + .as('t'); + }); + }) + .toString(), // this selects all tasks for the environment and returns everything outside of the requested retain days value - selectTaskHistoryRetentionDays: (environment: number, retain: number) => - knex('task') - .select('id','name','remote_id') - .where('environment', environment) - .where('admin_only_view', 0) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' DAY')) - .toString(), + selectTaskHistoryRetentionDays: (environment: number, retain: number) => knex('task') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where('admin_only_view', 0) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} DAY`)) + .toString(), // this selects all tasks for the environment and returns everything outside of the requested retain months value - selectTaskHistoryRetentionMonths: (environment: number, retain: number) => - knex('task') - .select('id','name','remote_id') - .where('environment', environment) - .where('admin_only_view', 0) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' MONTH')) - .toString(), + selectTaskHistoryRetentionMonths: (environment: number, retain: number) => knex('task') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where('admin_only_view', 0) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} MONTH`)) + .toString(), // this selects all tasks for the environment and returns everything - selectTaskHistoryForEnvironment: (environment: number) => - knex('task') - .where('environment', '=', environment) - .toString(), + selectTaskHistoryForEnvironment: (environment: number) => knex('task') + .where('environment', '=', environment) + .toString(), // same as select, except it deletes all tasks for the environment outside of the requested retain value - deleteTaskHistory: (environment: number, retain: number) => - knex('task') - .select('id','name','remote_id') - .where('environment', environment) - .where('admin_only_view', 0) - .whereNotIn('id', function() { - this.select('id') - .from(function() { - this.select('id') - .from('task') - .where('environment', environment) - .where('admin_only_view', 0) - .orderBy('id','desc') - .limit(retain) - .as('t') - }) - }) - .delete() - .toString(), + deleteTaskHistory: (environment: number, retain: number) => knex('task') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where('admin_only_view', 0) + .whereNotIn('id', function () { + this.select('id') + .from(function () { + this.select('id') + .from('task') + .where('environment', environment) + .where('admin_only_view', 0) + .orderBy('id', 'desc') + .limit(retain) + .as('t'); + }); + }) + .delete() + .toString(), // same as select, except it deletes all tasks for the environment outside of the requested retain value - deleteTaskHistoryDays: (environment: number, retain: number) => - knex('task') - .select('id','name','remote_id') - .where('environment', environment) - .where('admin_only_view', 0) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' DAY')) - .delete() - .toString(), + deleteTaskHistoryDays: (environment: number, retain: number) => knex('task') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where('admin_only_view', 0) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} DAY`)) + .delete() + .toString(), // same as select, except it deletes all tasks for the environment outside of the requested retain value - deleteTaskHistoryMonths: (environment: number, retain: number) => - knex('task') - .select('id','name','remote_id') - .where('environment', environment) - .where('admin_only_view', 0) - .where(knex.raw('created <= NOW() - INTERVAL ' + retain + ' MONTH')) - .delete() - .toString(), + deleteTaskHistoryMonths: (environment: number, retain: number) => knex('task') + .select('id', 'name', 'remote_id') + .where('environment', environment) + .where('admin_only_view', 0) + .where(knex.raw(`created <= NOW() - INTERVAL ${retain} MONTH`)) + .delete() + .toString(), // same as select, except it deletes all tasks for the environment outside of the requested retain value - deleteTaskHistoryForEnvironment: (environment: number) => - knex('task') - .where('environment', '=', environment) - .delete() - .toString(), + deleteTaskHistoryForEnvironment: (environment: number) => knex('task') + .where('environment', '=', environment) + .delete() + .toString(), }; diff --git a/services/api/src/resources/task/task_definition_resolvers.ts b/services/api/src/resources/task/task_definition_resolvers.ts index 0cb76f1b6e..9e847b3138 100644 --- a/services/api/src/resources/task/task_definition_resolvers.ts +++ b/services/api/src/resources/task/task_definition_resolvers.ts @@ -13,13 +13,13 @@ import { AdvancedTaskDefinitionInterface, AdvancedTaskDefinitionType, isAdvancedTaskDefinitionSystemLevelTask, - getAdvancedTaskDefinitionType + getAdvancedTaskDefinitionType, } from './models/taskRegistration'; -import * as advancedTaskArgument from './models/advancedTaskDefinitionArgument' +import * as advancedTaskArgument from './models/advancedTaskDefinitionArgument'; import * as advancedTaskToolbox from './advancedtasktoolbox'; import { KeycloakUnauthorizedError } from '../../util/auth'; -import { generateTaskName } from '@lagoon/commons/dist/util/lagoon'; -import { TaskSourceType } from '@lagoon/commons/dist/types'; +import { generateTaskName } from '../../commons/util/lagoon'; +import { TaskSourceType } from '../../commons/types'; enum AdvancedTaskDefinitionTarget { Group, @@ -30,63 +30,65 @@ enum AdvancedTaskDefinitionTarget { const TASK_PERMISSION_LEVELS = ['GUEST', 'MAINTAINER', 'DEVELOPER']; -const taskStatusTypeToString = R.cond([ +const _taskStatusTypeToString = R.cond([ [R.equals('ACTIVE'), R.toLower], [R.equals('SUCCEEDED'), R.toLower], [R.equals('FAILED'), R.toLower], - [R.T, R.identity] + [R.T, R.identity], ]); -const PermissionsToRBAC = (permission: string) => { - return `invoke:${permission.toLowerCase()}`; -}; +const PermissionsToRBAC = (permission: string) => `invoke:${permission.toLowerCase()}`; -export const allAdvancedTaskDefinitions = async (root, args, {sqlClientPool, hasPermission, models, adminScopes}) => { - //is the user a system admin? +export const allAdvancedTaskDefinitions = async (root, args, { + sqlClientPool, hasPermission, models, adminScopes, +}) => { + // is the user a system admin? try { - await hasPermission('advanced_task','create:advanced'); - } catch(e) { - throw new KeycloakUnauthorizedError("Only system admins have access to view all advanced task definitions"); + await hasPermission('advanced_task', 'create:advanced'); + } catch (_e) { + throw new KeycloakUnauthorizedError('Only system admins have access to view all advanced task definitions'); } - let adTaskDefs = await query( + const adTaskDefs = await query( sqlClientPool, - Sql.selectAdvancedTaskDefinitions() + Sql.selectAdvancedTaskDefinitions(), ); const atf = advancedTaskToolbox.advancedTaskFunctions(sqlClientPool, models, hasPermission, adminScopes); - for(let i = 0; i < adTaskDefs.length; i++) { + for (let i = 0; i < adTaskDefs.length; i++) { adTaskDefs[i].advancedTaskDefinitionArguments = await atf.advancedTaskDefinitionArguments(adTaskDefs[i].id); } return adTaskDefs; -} +}; export const advancedTaskDefinitionById = async ( root, { id }, - { sqlClientPool, hasPermission, models, adminScopes } + { + sqlClientPool, hasPermission, models, adminScopes, + }, ) => { - const atf = advancedTaskToolbox.advancedTaskFunctions(sqlClientPool, models, hasPermission, adminScopes); await hasPermission('task', 'view', {}); const advancedTaskDef = await atf.advancedTaskDefinitionById( - id + id, ); - if(await atf.permissions.canUserSeeTaskDefinition(advancedTaskDef) == false) { - throw new Error("You do not have permission"); + if (await atf.permissions.canUserSeeTaskDefinition(advancedTaskDef) === false) { + throw new Error('You do not have permission'); } return advancedTaskDef; }; - export const getRegisteredTasksByEnvironmentId = async ( { id }, {}, - { sqlClientPool, hasPermission, models, adminScopes } + { + sqlClientPool, hasPermission, models, adminScopes, + }, ) => { let rows; @@ -94,7 +96,9 @@ export const getRegisteredTasksByEnvironmentId = async ( rows = await resolveTasksForEnvironment( {}, { environment: id }, - { sqlClientPool, hasPermission, models, adminScopes } + { + sqlClientPool, hasPermission, models, adminScopes, + }, ); rows = await Filters.filterAdminTasks(hasPermission, rows); @@ -106,103 +110,104 @@ export const getRegisteredTasksByEnvironmentId = async ( export const resolveTasksForEnvironment = async ( root, { environment }, - { sqlClientPool, hasPermission, models, adminScopes } + { + sqlClientPool, hasPermission, models, adminScopes, + }, ) => { const environmentDetails = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environment); if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('task', 'view', { - project: environmentDetails.project + project: environmentDetails.project, }); } - let environmentRows = await query( + const environmentRows = await query( sqlClientPool, - Sql.selectAdvancedTaskDefinitionsForEnvironment(environment) + Sql.selectAdvancedTaskDefinitionsForEnvironment(environment), ); const proj = await projectHelpers(sqlClientPool).getProjectByEnvironmentId( - environment + environment, ); - let projectRows = await query( + const projectRows = await query( sqlClientPool, - Sql.selectAdvancedTaskDefinitionsForProject(proj.project) + Sql.selectAdvancedTaskDefinitionsForProject(proj.project), ); - const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, proj.projectId) + const projectGroups = await groupHelpers(sqlClientPool).selectGroupsByProjectId(models, proj.projectId); const projectGroupsFiltered = R.pluck('name', projectGroups); - let groupRows = await query( + const groupRows = await query( sqlClientPool, - Sql.selectAdvancedTaskDefinitionsForGroups(projectGroupsFiltered) + Sql.selectAdvancedTaskDefinitionsForGroups(projectGroupsFiltered), ); const systemWideRows = await query( sqlClientPool, - Sql.selectAdvancedTaskDefinitionsForSystem() + Sql.selectAdvancedTaskDefinitionsForSystem(), ); - //@ts-ignore - let totalRows = R.concat(R.concat(R.concat(environmentRows, projectRows), groupRows), systemWideRows); + // @ts-ignore + const totalRows = R.concat(R.concat(R.concat(environmentRows, projectRows), groupRows), systemWideRows); - //@ts-ignore + // @ts-ignore let rows = R.uniqBy(o => o.name, totalRows); - //now we filter the permissions + // now we filter the permissions const currentUsersPermissionForProject = await currentUsersAdvancedTaskRBACRolesForProject( hasPermission, - proj.projectId + proj.projectId, ); - //@ts-ignore + // @ts-ignore rows = R.filter(e => currentUsersPermissionForProject.includes(e.permission), rows); const atf = advancedTaskToolbox.advancedTaskFunctions(sqlClientPool, models, hasPermission, adminScopes); - let typeValidatorFactory = advancedTaskArgument.advancedTaskDefinitionTypeFactory(sqlClientPool, null, environment); + const typeValidatorFactory = advancedTaskArgument.advancedTaskDefinitionTypeFactory(sqlClientPool, null, environment); // TODO: this needs to be somehow refactored into all lookups. // we might need a "load task" function or something. - for(let i = 0; i < rows.length; i++ ) { - //@ts-ignore - let argsForTask = await atf.advancedTaskDefinitionArguments(rows[i].id); - let processedArgs = []; - for(let i = 0; i < argsForTask.length; i++) { - let processing = argsForTask[i]; - let validator: advancedTaskArgument.ArgumentBase = typeValidatorFactory(processing.type); + for (let i = 0; i < rows.length; i++) { + // @ts-ignore + const argsForTask = await atf.advancedTaskDefinitionArguments(rows[i].id); + const processedArgs = []; + for (let i = 0; i < argsForTask.length; i++) { + const processing = argsForTask[i]; + const validator: advancedTaskArgument.ArgumentBase = typeValidatorFactory(processing.type); processing.range = await validator.getArgumentRange(); processedArgs.push(processing); } - //@ts-ignore + // @ts-ignore rows[i].advancedTaskDefinitionArguments = processedArgs; } return rows; }; - const currentUsersAdvancedTaskRBACRolesForProject = async ( hasPermission, - projectId: number + projectId: number, ) => { const rbacPermissions = TASK_PERMISSION_LEVELS; - let effectivePermissions = []; + const effectivePermissions = []; for (let i = 0; i < rbacPermissions.length; i++) { try { await hasPermission( 'advanced_task', PermissionsToRBAC(rbacPermissions[i]), { - project: projectId - } + project: projectId, + }, ); effectivePermissions.push(rbacPermissions[i]); - } catch (ex) { - //we do nothing if this fails ... + } catch (_ex) { + // we do nothing if this fails ... } } return effectivePermissions; @@ -211,16 +216,16 @@ const currentUsersAdvancedTaskRBACRolesForProject = async ( export const advancedTaskDefinitionArgumentById = async ( root, id, - { sqlClientPool, hasPermission, adminScopes } + { sqlClientPool, hasPermission, adminScopes }, ) => { const rows = await query( sqlClientPool, - Sql.selectAdvancedTaskDefinitionArgumentById(id) + Sql.selectAdvancedTaskDefinitionArgumentById(id), ); // if the user is not a platform owner or viewer, then perform normal permission check if (!adminScopes.platformOwner && !adminScopes.platformViewer) { await hasPermission('environment', 'view', { - project: id + project: id, }); } @@ -230,11 +235,12 @@ export const advancedTaskDefinitionArgumentById = async ( export const addAdvancedTaskDefinition = async ( root, { - input + input, + }, + { + sqlClientPool, hasPermission, models, userActivityLogger, adminScopes, }, - { sqlClientPool, hasPermission, models, userActivityLogger, adminScopes } ) => { - const { name, description, @@ -256,13 +262,13 @@ export const addAdvancedTaskDefinition = async ( } = input; const atb = advancedTaskToolbox.advancedTaskFunctions( - sqlClientPool, models, hasPermission, adminScopes + sqlClientPool, models, hasPermission, adminScopes, ); - let projectObj = await getProjectByEnvironmentIdOrProjectId( + const projectObj = await getProjectByEnvironmentIdOrProjectId( sqlClientPool, environment, - project + project, ); input.deployTokenInjection = input.deployTokenInjection || false; @@ -276,23 +282,20 @@ export const addAdvancedTaskDefinition = async ( // We need to validate the incoming data // first, we actually only want either groupName, environment, or project - if(environment && groupName || environment && project || project && groupName) { - throw Error("Only one of `environment`, `project`, or `groupName` should be set when creating a custom task."); + if (environment && groupName || environment && project || project && groupName) { + throw Error('Only one of `environment`, `project`, or `groupName` should be set when creating a custom task.'); } // If we have a system wide task, there should be no group/env/project - if(systemWide == true && (environment || groupName || project)) { - throw Error("When creating a system wide task, you cannot specify a group/project/environment"); + if (systemWide === true && (environment || groupName || project)) { + throw Error('When creating a system wide task, you cannot specify a group/project/environment'); } - if(systemWide == false && (environment == null && groupName == null && project == null)) { + if (systemWide === false && (environment == null && groupName == null && project == null)) { throw Error("If you're trying to define a system wide task, set 'systemWide' to true, else set a target"); } - - - - //let's see if there's already an advanced task definition with this name ... + // let's see if there's already an advanced task definition with this name ... // Note: this will all be scoped to either System, group, project, or environment // hence the filters below. const rows = await query( @@ -302,22 +305,21 @@ export const addAdvancedTaskDefinition = async ( project, environment, groupName, - systemWide - ) + systemWide, + ), ); - let taskDef = R.prop(0, rows); + const taskDef = R.prop(0, rows); if (taskDef) { // At this point, `taskDefMatchedIncoming` will indicate // whether the incoming details for a similarly named // task _scoped to the system/group/project/environment_ // exists. If it does, we return its id instead of creating it. - const taskDefMatchesIncoming = - taskDef.description == description && - taskDef.image == image && - taskDef.type == type && - (taskDef.type == AdvancedTaskDefinitionType.image || - taskDef.command == command); + const taskDefMatchesIncoming = taskDef.description === description + && taskDef.image === image + && taskDef.type === type + && (taskDef.type === AdvancedTaskDefinitionType.image + || taskDef.command === command); // if the similarly named task (scoped to system/group/project/environment) does // not match the existing definition, we have to reject this request. @@ -339,7 +341,6 @@ export const addAdvancedTaskDefinition = async ( return taskDef; } - const { insertId } = await query( sqlClientPool, Sql.insertAdvancedTaskDefinition({ @@ -360,16 +361,16 @@ export const addAdvancedTaskDefinition = async ( project_key_injection: projectKeyInjection, admin_only_view: adminOnlyView, system_wide: systemWide, - }) + }), ); - //now attach arguments - if(advancedTaskDefinitionArguments) { - for(let i = 0; i < advancedTaskDefinitionArguments.length; i++) { + // now attach arguments + if (advancedTaskDefinitionArguments) { + for (let i = 0; i < advancedTaskDefinitionArguments.length; i++) { if (advancedTaskDefinitionArguments[i].defaultValue) { - let typeValidatorFactory = advancedTaskArgument.advancedTaskDefinitionTypeFactory(sqlClientPool, null, environment); - let validator: advancedTaskArgument.ArgumentBase = typeValidatorFactory(advancedTaskDefinitionArguments[i].type); - if(!(await validator.validateInput(advancedTaskDefinitionArguments[i].defaultValue))) { + const typeValidatorFactory = advancedTaskArgument.advancedTaskDefinitionTypeFactory(sqlClientPool, null, environment); + const validator: advancedTaskArgument.ArgumentBase = typeValidatorFactory(advancedTaskDefinitionArguments[i].type); + if (!(await validator.validateInput(advancedTaskDefinitionArguments[i].defaultValue))) { throw new Error(`Invalid input defaultValue "${advancedTaskDefinitionArguments[i].defaultValue}" for type "${advancedTaskDefinitionArguments[i].type}" given for argument "${advancedTaskDefinitionArguments[i].name}"`); } } @@ -383,7 +384,7 @@ export const addAdvancedTaskDefinition = async ( defaultValue: advancedTaskDefinitionArguments[i].defaultValue, optional: advancedTaskDefinitionArguments[i].optional, displayName: advancedTaskDefinitionArguments[i].displayName, - }) + }), ); } } @@ -392,13 +393,13 @@ export const addAdvancedTaskDefinition = async ( project: '', event: 'api:addTaskDefinition', payload: { - project: project, - taskDef: insertId - } + project, + taskDef: insertId, + }, }); return await atb.advancedTaskDefinitionById( - insertId + insertId, ); }; @@ -412,7 +413,7 @@ export const updateAdvancedTaskDefinition = async ( name, description, image = '', - type, + _type, service, command, permission, @@ -421,36 +422,37 @@ export const updateAdvancedTaskDefinition = async ( deployTokenInjection, projectKeyInjection, adminOnlyView, - } - } + }, + }, + }, + { + sqlClientPool, hasPermission, models, userActivityLogger, adminScopes, }, - { sqlClientPool, hasPermission, models, userActivityLogger, adminScopes } ) => { if (isPatchEmpty({ patch })) { throw new Error('Input patch requires at least 1 attribute'); } const atb = advancedTaskToolbox.advancedTaskFunctions( - sqlClientPool, models, hasPermission, adminScopes + sqlClientPool, models, hasPermission, adminScopes, ); let task = await atb.advancedTaskDefinitionById(id); - let projectObj = await getProjectByEnvironmentIdOrProjectId( + const projectObj = await getProjectByEnvironmentIdOrProjectId( sqlClientPool, task.environment, - task.project + task.project, ); - await checkAdvancedTaskPermissions(task, hasPermission, models, projectObj); // we will merge the patch into the advanced task to double check that the final data is still valid - task = {...task, ...patch}; + task = { ...task, ...patch }; validateAdvancedTaskDefinitionData(task, task.image, task.command, task.type); - //We actually don't want them to be able to update group, project, environment - so those aren't + // We actually don't want them to be able to update group, project, environment - so those aren't await query( sqlClientPool, Sql.updateAdvancedTaskDefinition({ @@ -466,20 +468,21 @@ export const updateAdvancedTaskDefinition = async ( deploy_token_injection: deployTokenInjection, project_key_injection: projectKeyInjection, admin_only_view: adminOnlyView, - } - }) + }, + }), ); + // eslint-disable-next-line no-useless-catch try { if (advancedTaskDefinitionArguments) { - //remove current arguments from task defintion before we add new ones + // remove current arguments from task defintion before we add new ones await query( sqlClientPool, - Sql.deleteAdvancedTaskDefinitionArgumentByTaskDef(id) + Sql.deleteAdvancedTaskDefinitionArgumentByTaskDef(id), ); - //add advanced task definition arguments - for(let i = 0; i < advancedTaskDefinitionArguments.length; i++) { + // add advanced task definition arguments + for (let i = 0; i < advancedTaskDefinitionArguments.length; i++) { await query( sqlClientPool, Sql.insertAdvancedTaskDefinitionArgument({ @@ -489,8 +492,8 @@ export const updateAdvancedTaskDefinition = async ( displayName: advancedTaskDefinitionArguments[i].displayName, defaultValue: advancedTaskDefinitionArguments[i].defaultValue, optional: advancedTaskDefinitionArguments[i].optional, - type: advancedTaskDefinitionArguments[i].type - }) + type: advancedTaskDefinitionArguments[i].type, + }), ); } } @@ -500,26 +503,25 @@ export const updateAdvancedTaskDefinition = async ( event: 'api:updateTaskDefinition', payload: { project: task.project, - taskDef: id - } + taskDef: id, + }, }); const atf = advancedTaskToolbox.advancedTaskFunctions(sqlClientPool, models, hasPermission, adminScopes); return await atf.advancedTaskDefinitionById(id); } catch (error) { - throw error + throw error; } -} - +}; const getProjectByEnvironmentIdOrProjectId = async ( sqlClientPool, environment, - project + project, ) => { if (environment) { - let projByEnv = await projectHelpers(sqlClientPool).getProjectByEnvironmentId( - environment + const projByEnv = await projectHelpers(sqlClientPool).getProjectByEnvironmentId( + environment, ); return await projectHelpers(sqlClientPool).getProjectById(projByEnv.project); } @@ -531,133 +533,137 @@ const getProjectByEnvironmentIdOrProjectId = async ( export const invokeRegisteredTask = async ( root, - { advancedTaskDefinition, environment, argumentValues, sourceType }, - { sqlClientPool, hasPermission, models, keycloakGrant, legacyGrant, adminScopes } + { + advancedTaskDefinition, environment, argumentValues, sourceType, + }, + { + sqlClientPool, hasPermission, models, keycloakGrant, legacyGrant, adminScopes, + }, ) => { await envValidators(sqlClientPool).environmentExists(environment); - let task = await getNamedAdvancedTaskForEnvironment( + const task = await getNamedAdvancedTaskForEnvironment( sqlClientPool, hasPermission, advancedTaskDefinition, environment, models, - adminScopes + adminScopes, ); const atb = advancedTaskToolbox.advancedTaskFunctions( - sqlClientPool, models, hasPermission, adminScopes + sqlClientPool, models, hasPermission, adminScopes, ); - //here we want to validate the incoming arguments - let taskArgs = await atb.advancedTaskDefinitionArguments(task.id); + // here we want to validate the incoming arguments + const taskArgs = await atb.advancedTaskDefinitionArguments(task.id); - //let's grab something that'll be able to tell us whether our arguments - //are valid + // let's grab something that'll be able to tell us whether our arguments + // are valid const typeValidatorFactory = advancedTaskArgument.advancedTaskDefinitionTypeFactory(sqlClientPool, task, environment); - if(argumentValues) { - for(let i = 0; i < argumentValues.length; i++) { - //grab the type for this one - let {advancedTaskDefinitionArgumentName, value} = argumentValues[i]; - let taskArgDef = R.find(R.propEq('name', advancedTaskDefinitionArgumentName))(taskArgs); - if(!taskArgDef) { + if (argumentValues) { + for (let i = 0; i < argumentValues.length; i++) { + // grab the type for this one + const { advancedTaskDefinitionArgumentName, value: argValue } = argumentValues[i]; + let value = argValue; + const taskArgDef = R.find(R.propEq(advancedTaskDefinitionArgumentName, 'name' as any))(taskArgs); + if (!taskArgDef) { throw new Error(`Cannot find argument type named ${advancedTaskDefinitionArgumentName}`); } // if the value is empty and there is a default value on the argument - if (value.trim() === "" && taskArgDef["defaultValue"]) { - value = taskArgDef["defaultValue"] + if (value.trim() === '' && taskArgDef.defaultValue) { + value = taskArgDef.defaultValue; } - //@ts-ignore - let validator: advancedTaskArgument.ArgumentBase = typeValidatorFactory(taskArgDef.type); + // @ts-ignore + const validator: advancedTaskArgument.ArgumentBase = typeValidatorFactory(taskArgDef.type); - if(!(await validator.validateInput(value))) { - //@ts-ignore + if (!(await validator.validateInput(value))) { + // @ts-ignore throw new Error(`Invalid input "${value}" for type "${taskArgDef.type}" given for argument "${advancedTaskDefinitionArgumentName}"`); } - }; + } } - const environmentDetails = await environmentHelpers( - sqlClientPool + sqlClientPool, ).getEnvironmentById(environment); await hasPermission('advanced_task', PermissionsToRBAC(task.permission), { - project: environmentDetails.project + project: environmentDetails.project, }); switch (task.type) { - case TaskRegistration.TYPE_STANDARD: + case TaskRegistration.TYPE_STANDARD: - let taskCommandEnvs = ''; - let taskCommand = ""; + let taskCommandEnvs = ''; + let taskCommand = ''; - if(argumentValues && argumentValues.length > 0) { - taskCommandEnvs = R.reduce((acc, val) => { - //@ts-ignore - return `${acc} ${val.advancedTaskDefinitionArgumentName}="${val.value}"` - }, taskCommandEnvs, argumentValues); + if (argumentValues && argumentValues.length > 0) { + taskCommandEnvs = R.reduce((acc, val) => + // @ts-ignore + `${acc} ${val.advancedTaskDefinitionArgumentName}="${val.value}"`, + taskCommandEnvs, + argumentValues); - taskCommand += `${taskCommandEnvs}; `; - } + taskCommand += `${taskCommandEnvs}; `; + } - taskCommand += `${task.command}`; - sourceType ??= TaskSourceType.API - const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant) - const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ - name: task.name, - taskName: generateTaskName(), - environment: environment, - service: task.service, - command: taskCommand, - deployTokenInjection: task.deployTokenInjection, - projectKeyInjection: task.projectKeyInjection, - adminOnlyView: task.adminOnlyView, - execute: true, - sourceType: sourceType, - sourceUser: sourceUser, - }); - return taskData; - break; - case TaskRegistration.TYPE_ADVANCED: - // the return data here is basically what gets dropped into the DB. - - // get any arguments ready for payload - let payload = {}; - if(argumentValues) { - for(let i = 0; i < argumentValues.length; i++) { - //@ts-ignore - payload[argumentValues[i].advancedTaskDefinitionArgumentName] = argumentValues[i].value; - } + taskCommand += `${task.command}`; + sourceType ??= TaskSourceType.API; + const sourceUser = await deploymentHelpers(sqlClientPool).getSourceUser(keycloakGrant, legacyGrant); + const taskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addTask({ + name: task.name, + taskName: generateTaskName(), + environment, + service: task.service, + command: taskCommand, + deployTokenInjection: task.deployTokenInjection, + projectKeyInjection: task.projectKeyInjection, + adminOnlyView: task.adminOnlyView, + execute: true, + sourceType, + sourceUser, + }); + return taskData; + break; + case TaskRegistration.TYPE_ADVANCED: + // the return data here is basically what gets dropped into the DB. + + // get any arguments ready for payload + const payload = {}; + if (argumentValues) { + for (let i = 0; i < argumentValues.length; i++) { + // @ts-ignore + payload[argumentValues[i].advancedTaskDefinitionArgumentName] = argumentValues[i].value; } + } + const advancedTaskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addAdvancedTask({ + name: task.name, + taskName: generateTaskName(), + created: undefined, + started: undefined, + completed: undefined, + environment, + service: task.service || 'cli', + image: task.image, // the return data here is basically what gets dropped into the DB. + payload, + deployTokenInjection: task.deployTokenInjection, + projectKeyInjection: task.projectKeyInjection, + adminOnlyView: task.adminOnlyView, + remoteId: undefined, + execute: true, + }); - const advancedTaskData = await Helpers(sqlClientPool, hasPermission, adminScopes).addAdvancedTask({ - name: task.name, - taskName: generateTaskName(), - created: undefined, - started: undefined, - completed: undefined, - environment, - service: task.service || 'cli', - image: task.image, //the return data here is basically what gets dropped into the DB. - payload: payload, - deployTokenInjection: task.deployTokenInjection, - projectKeyInjection: task.projectKeyInjection, - adminOnlyView: task.adminOnlyView, - remoteId: undefined, - execute: true, - }); - - return advancedTaskData; - break; - default: - throw new Error('Cannot find matching task'); - break; - } + return advancedTaskData; + break; + default: + throw new Error('Cannot find matching task'); + break; + } }; const getNamedAdvancedTaskForEnvironment = async ( @@ -666,68 +672,71 @@ const getNamedAdvancedTaskForEnvironment = async ( advancedTaskDefinition, environment, models, - adminScopes + adminScopes, ):Promise => { let rows; rows = await resolveTasksForEnvironment( {}, { environment }, - { sqlClientPool, hasPermission, models, adminScopes } + { + sqlClientPool, hasPermission, models, adminScopes, + }, ); rows = await Filters.filterAdminTasks(hasPermission, rows); - //@ts-ignore - const taskDef = R.find(o => o.id == advancedTaskDefinition, rows); - if (taskDef == undefined) { + // @ts-ignore + const taskDef = R.find(o => o.id === advancedTaskDefinition, rows); + if (taskDef === undefined) { throw new Error( - `Task registration '${advancedTaskDefinition}' could not be found.` + `Task registration '${advancedTaskDefinition}' could not be found.`, ); } - //@ts-ignore + // @ts-ignore return taskDef; }; - export const deleteAdvancedTaskDefinition = async ( root, { advancedTaskDefinition }, - { sqlClientPool, hasPermission, models, adminScopes } + { + sqlClientPool, hasPermission, models, adminScopes, + }, ) => { - //load up advanced task definition ... + // load up advanced task definition ... const atb = advancedTaskToolbox.advancedTaskFunctions( - sqlClientPool, models, hasPermission, adminScopes + sqlClientPool, models, hasPermission, adminScopes, ); const adTaskDef = await atb.advancedTaskDefinitionById(advancedTaskDefinition); if (!adTaskDef) { throw new Error( - `Advanced Task ID ${addAdvancedTaskDefinition} cannot be loaded` + `Advanced Task ID ${addAdvancedTaskDefinition} cannot be loaded`, ); } - //determine type and check user perms ... + // determine type and check user perms ... switch (getAdvancedTaskTarget(adTaskDef)) { case AdvancedTaskDefinitionTarget.Environment: case AdvancedTaskDefinitionTarget.Project: - let projectObj = await getProjectByEnvironmentIdOrProjectId( + const projectObj = await getProjectByEnvironmentIdOrProjectId( sqlClientPool, adTaskDef.environment, - adTaskDef.project + adTaskDef.project, ); - await hasPermission('task', `add:production`, { - project: projectObj.id + await hasPermission('task', 'add:production', { + project: projectObj.id, }); break; case AdvancedTaskDefinitionTarget.Group: const group = await models.GroupModel.loadGroupByIdOrName({ - name: adTaskDef.groupName + name: adTaskDef.groupName, }); await hasPermission('group', 'update', { - group: group.id + group: group.id, }); break; case AdvancedTaskDefinitionTarget.SystemWide: @@ -737,20 +746,19 @@ export const deleteAdvancedTaskDefinition = async ( throw Error('Image Tasks are not yet supported'); } - const rows = await query( + const _rows = await query( sqlClientPool, - Sql.selectPermsForTask(advancedTaskDefinition) + Sql.selectPermsForTask(advancedTaskDefinition), ); - await query( sqlClientPool, - Sql.deleteAdvancedTaskDefinitionArgumentsForTask(advancedTaskDefinition) + Sql.deleteAdvancedTaskDefinitionArgumentsForTask(advancedTaskDefinition), ); await query( sqlClientPool, - Sql.deleteAdvancedTaskDefinition(advancedTaskDefinition) + Sql.deleteAdvancedTaskDefinition(advancedTaskDefinition), ); return 'success'; @@ -759,34 +767,32 @@ export const deleteAdvancedTaskDefinition = async ( const getAdvancedTaskTarget = advancedTask => { if (advancedTask.environment != null) { return AdvancedTaskDefinitionTarget.Environment; - } else if (advancedTask.project != null) { + } if (advancedTask.project != null) { return AdvancedTaskDefinitionTarget.Project; - } else if (advancedTask.groupName != null) { + } if (advancedTask.groupName != null) { return AdvancedTaskDefinitionTarget.Group; - } else { - return AdvancedTaskDefinitionTarget.SystemWide; } + return AdvancedTaskDefinitionTarget.SystemWide; }; - function validateAdvancedTaskDefinitionData(input: any, image: any, command: any, type: any) { switch (getAdvancedTaskDefinitionType(input)) { case AdvancedTaskDefinitionType.image: - if (!image || 0 === image.length) { + if (!image || image.length === 0) { throw new Error( - 'Unable to create image based task with no image supplied' + 'Unable to create image based task with no image supplied', ); } break; case AdvancedTaskDefinitionType.command: - if (!command || 0 === command.length) { + if (!command || command.length === 0) { throw new Error('Unable to create Advanced task definition - no command provided'); } break; default: throw new Error( - 'Undefined Advanced Task Definition type passed at creation time: ' + - type + `Undefined Advanced Task Definition type passed at creation time: ${ + type}`, ); break; } @@ -795,21 +801,21 @@ function validateAdvancedTaskDefinitionData(input: any, image: any, command: any async function checkAdvancedTaskPermissions(input:AdvancedTaskDefinitionInterface, hasPermission: any, models: any, projectObj: any) { if (isAdvancedTaskDefinitionSystemLevelTask(input)) { hasPermission('advanced_task', 'create:advanced'); - } else if (getAdvancedTaskDefinitionType(input) == AdvancedTaskDefinitionType.image || input.deployTokenInjection != false || input.adminOnlyView != false) { - //We're only going to allow administrators to add these for now ... + } else if (getAdvancedTaskDefinitionType(input) === AdvancedTaskDefinitionType.image || input.deployTokenInjection !== false || input.adminOnlyView !== false) { + // We're only going to allow administrators to add these for now ... await hasPermission('advanced_task', 'create:advanced'); } else if (input.groupName) { const group = await models.GroupModel.loadGroupByIdOrName({ - name: input.groupName + name: input.groupName, }); await hasPermission('group', 'update', { - group: group.id + group: group.id, }); } else if (projectObj) { - //does the user have permission to actually add to this? - //i.e. are they a maintainer? - await hasPermission('task', `add:production`, { - project: projectObj.id + // does the user have permission to actually add to this? + // i.e. are they a maintainer? + await hasPermission('task', 'add:production', { + project: projectObj.id, }); } } diff --git a/services/api/src/resources/user/resolvers.ts b/services/api/src/resources/user/resolvers.ts index 9af29ced02..7a30dacce8 100644 --- a/services/api/src/resources/user/resolvers.ts +++ b/services/api/src/resources/user/resolvers.ts @@ -1,17 +1,17 @@ import * as R from 'ramda'; -import { ResolverFn } from '../'; +import { ResolverFn } from '..'; import { query, isPatchEmpty } from '../../util/db'; import { Helpers as organizationHelpers } from '../organization/helpers'; import { Sql } from './sql'; -import { AuditType } from '@lagoon/commons/dist/types'; +import { AuditType } from '../../commons/types'; import { AuditLog } from '../audit/types'; -import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger'; +import { sendToLagoonLogs } from '../../commons/logs/lagoon-logger'; import { UserModel, User } from '../../models/user'; export const getMe: ResolverFn = async (_root, args, { models, keycloakGrant: grant }) => { const currentUserId: string = grant.access_token.content.sub; return models.UserModel.loadUserById(currentUserId); -} +}; class SearchInputError extends Error { constructor(message: string) { @@ -27,7 +27,6 @@ class UserNotFoundError extends Error { } } - export const getUserBySshKey: ResolverFn = async ( _root, { sshKey }, @@ -41,7 +40,7 @@ export const getUserBySshKey: ResolverFn = async ( // @ts-ignore )(sshKey); - if(!keyType || !keyValue) { + if (!keyType || !keyValue) { throw new SearchInputError("Malformed ssh key provided. Should begin with key-type (eg. ssh-rsa|ssh-ed25519|etc.), then a space, then the key's value"); } @@ -63,19 +62,19 @@ export const getUserBySshFingerprint: ResolverFn = async ( ) => { await hasPermission('user', 'getBySshKey'); - if(!fingerprint) { - throw new SearchInputError("Malformed ssh key fingerprint provided"); + if (!fingerprint) { + throw new SearchInputError('Malformed ssh key fingerprint provided'); } try { const rows = await query( sqlClientPool, - Sql.selectUserIdBySshFingerprint({keyFingerprint: fingerprint}), + Sql.selectUserIdBySshFingerprint({ keyFingerprint: fingerprint }), ); const userId = R.map(R.prop('usid'), rows); const user = await models.UserModel.loadUserById(userId[0]); return user; - } catch (err) { - throw new UserNotFoundError("No user found matching provided fingerprint"); + } catch (_err) { + throw new UserNotFoundError('No user found matching provided fingerprint'); } }; @@ -83,7 +82,7 @@ export const getUserBySshFingerprint: ResolverFn = async ( export const getAllUsers: ResolverFn = async ( _root, { id, email, gitlabId }, - { sqlClientPool, models, hasPermission }, + { models, hasPermission }, ) => { await hasPermission('user', 'viewAll'); @@ -91,28 +90,26 @@ export const getAllUsers: ResolverFn = async ( try { // use the model to get a user by email address instead of filtering const user = await models.UserModel.loadUserByEmail(email); - return [user] - } catch (e) { + return [user]; + } catch (_e) { // if no user found, return empty user list like before - return [] + return []; } } if (id) { try { // use the model to get a user by id instead of filtering const user = await models.UserModel.loadUserById(id); - return [user] - } catch (e) { + return [user]; + } catch (_e) { // if no user found, return empty user list like before - return [] + return []; } } // since gitlab users are harder to check, do the all users query and then filter them const users = await models.UserModel.loadAllUsers(); if (gitlabId) { - const filteredByGitlab = users.filter(function (item) { - return item.gitlabId === gitlabId; - }); + const filteredByGitlab = users.filter((item) => item.gitlabId === gitlabId); return filteredByGitlab; } @@ -123,14 +120,15 @@ export const getAllUsers: ResolverFn = async ( export const getUserByEmail: ResolverFn = async ( _root, { email }, - { sqlClientPool, models, hasPermission, keycloakGrant }, + { + models, hasPermission, keycloakGrant, + }, ) => { - const user = await models.UserModel.loadUserByEmail(email); if (keycloakGrant) { - if (keycloakGrant.access_token.content.sub == user.id) { + if (keycloakGrant.access_token.content.sub === user.id) { await hasPermission('ssh_key', 'view:user', { - users: [user.id] + users: [user.id], }); } else { await hasPermission('user', 'viewAll'); @@ -163,7 +161,7 @@ export const addUser: ResolverFn = async ( resource: { id: user.id, type: AuditType.USER, - details: input.email + details: input.email, }, }; userActivityLogger(`User added a user '${input.email}'`, { @@ -175,7 +173,7 @@ export const addUser: ResolverFn = async ( email: user.email, }, ...auditLog, - } + }, }); return user; @@ -234,7 +232,7 @@ export const resetUserPassword: ResolverFn = async ( resource: { id: user.id, type: AuditType.USER, - details: user.email + details: user.email, }, }; userActivityLogger(`User requested password reset '${user.email}'`, { @@ -246,7 +244,7 @@ export const resetUserPassword: ResolverFn = async ( email: user.email, }, ...auditLog, - } + }, }); return 'success'; @@ -272,7 +270,7 @@ export const deleteUser: ResolverFn = async ( resource: { id: user.id, type: AuditType.USER, - details: user.email + details: user.email, }, }; userActivityLogger(`User deleted a user '${user.email}'`, { @@ -284,7 +282,7 @@ export const deleteUser: ResolverFn = async ( email: user.email, }, ...auditLog, - } + }, }); return 'success'; @@ -293,21 +291,24 @@ export const deleteUser: ResolverFn = async ( // @DEPRECATED use addAdminToOrganization - addUserToOrganization adds a user as an organization owner export const addUserToOrganization: ResolverFn = async ( _root, - { input: { user: userInput, organization: organization, admin: admin, owner: owner } }, - { sqlClientPool, models, hasPermission, userActivityLogger }, + { + input: { + user: userInput, organization, admin, owner, + }, + }, + { + sqlClientPool, models, hasPermission, userActivityLogger, + }, ) => { - const organizationData = await organizationHelpers(sqlClientPool).getOrganizationById(organization); if (organizationData === undefined) { - let scope = "addViewer" + let scope = 'addViewer'; if (owner) { - scope = "addOwner" - } else { - if (admin) { - scope = "addOwner" - } + scope = 'addOwner'; + } else if (admin) { + scope = 'addOwner'; } - throw new Error(`Unauthorized: You don't have permission to "${scope}" on "organization"`) + throw new Error(`Unauthorized: You don't have permission to "${scope}" on "organization"`); } const user = await models.UserModel.loadUserByIdOrEmail({ @@ -315,28 +316,26 @@ export const addUserToOrganization: ResolverFn = async ( email: R.prop('email', userInput), }); - let updateUser = { + const updateUser = { id: user.id, - organization: organization, + organization, admin: false, owner: false, - } + }; if (owner) { await hasPermission('organization', 'addOwner', { - organization: organization + organization, + }); + updateUser.owner = true; + } else if (admin) { + await hasPermission('organization', 'addOwner', { + organization, }); - updateUser.owner = true + updateUser.admin = true; } else { - if (admin) { - await hasPermission('organization', 'addOwner', { - organization: organization - }); - updateUser.admin = true - } else { - await hasPermission('organization', 'addViewer', { - organization: organization - }); - } + await hasPermission('organization', 'addViewer', { + organization, + }); } await models.UserModel.updateUser(updateUser); @@ -349,16 +348,16 @@ export const addUserToOrganization: ResolverFn = async ( linkedResource: { id: user.id, type: AuditType.USER, - details: `${user.email} role ${(admin ? `admin: ${admin}` : owner ? `owner: ${owner}` : `viewer`)}`, + details: `${user.email} role ${(admin ? `admin: ${admin}` : owner ? `owner: ${owner}` : 'viewer')}`, }, organizationId: organizationData.id, }; - var emailDetails = await getUserOrgEmailDetails('api:addUserToOrganization', models, user, { + const emailDetails = await getUserOrgEmailDetails('api:addUserToOrganization', models, user, { Name: user.email, Email: user.email, OrganizationName: organizationData.name, - Role: `${(admin ? `admin: ${admin}` : owner ? `owner: ${owner}` : `viewer`)}` + Role: `${(admin ? `admin: ${admin}` : owner ? `owner: ${owner}` : 'viewer')}`, }); userActivityLogger(`User added a user to organization '${organizationData.name}'`, { @@ -368,29 +367,29 @@ export const addUserToOrganization: ResolverFn = async ( user: { id: user.id, email: user.email, - organization: organization, - admin: admin, - owner: owner, + organization, + admin, + owner, }, ...auditLog, ...emailDetails, - } + }, }); return organizationData; - }; // @DEPRECATED use removeAdminFromOrganization - removeUserFromOrganization a user as an organization owner export const removeUserFromOrganization: ResolverFn = async ( _root, - { input: { user: userInput, organization: organization } }, - { sqlClientPool, models, hasPermission, userActivityLogger }, + { input: { user: userInput, organization } }, + { + sqlClientPool, models, hasPermission, userActivityLogger, + }, ) => { - const organizationData = await organizationHelpers(sqlClientPool).getOrganizationById(organization); if (organizationData === undefined) { - throw new Error(`Unauthorized: You don't have permission to "addOwner" on "organization"`) + throw new Error('Unauthorized: You don\'t have permission to "addOwner" on "organization"'); } const user = await models.UserModel.loadUserByIdOrEmail({ @@ -399,12 +398,12 @@ export const removeUserFromOrganization: ResolverFn = async ( }); await hasPermission('organization', 'addOwner', { - organization: organization + organization, }); await models.UserModel.updateUser({ id: user.id, - organization: organization, + organization, remove: true, }); @@ -412,22 +411,21 @@ export const removeUserFromOrganization: ResolverFn = async ( resource: { id: organizationData.id.toString(), type: AuditType.ORGANIZATION, - details: organizationData.name + details: organizationData.name, }, linkedResource: { id: user.id, type: AuditType.USER, - details: user.email + details: user.email, }, organizationId: organizationData.id, }; - var emailDetails = await getUserOrgEmailDetails('api:removeUserFromOrganization', models, user, { - Name: user.email, - Email: user.email, - OrganizationName: organizationData.name, - }); - + const emailDetails = await getUserOrgEmailDetails('api:removeUserFromOrganization', models, user, { + Name: user.email, + Email: user.email, + OrganizationName: organizationData.name, + }); userActivityLogger(`User removed a user from organization '${organizationData.name}'`, { project: '', @@ -435,11 +433,11 @@ export const removeUserFromOrganization: ResolverFn = async ( payload: { user: { id: user.id, - organization: organization, + organization, }, ...auditLog, ...emailDetails, - } + }, }); return organizationData; @@ -448,39 +446,41 @@ export const removeUserFromOrganization: ResolverFn = async ( // addAdminToOrganization adds a user as an organization administrator export const addAdminToOrganization: ResolverFn = async ( _root, - { input: { user: userInput, organization: organization, role } }, - { sqlClientPool, models, hasPermission, userActivityLogger }, + { input: { user: userInput, organization, role } }, + { + sqlClientPool, models, hasPermission, userActivityLogger, + }, ) => { - let updateUser = { - id: "", + const updateUser = { + id: '', admin: false, owner: false, - organization: 0 - } - let scope = "addOwner" + organization: 0, + }; + let scope = 'addOwner'; switch (role) { - case "ADMIN": - scope = "addOwner" - updateUser.admin = true + case 'ADMIN': + scope = 'addOwner'; + updateUser.admin = true; break; - case "OWNER": - scope = "addOwner" - updateUser.owner = true + case 'OWNER': + scope = 'addOwner'; + updateUser.owner = true; break; - case "VIEWER": //fallthrough default + case 'VIEWER': // fallthrough default default: - scope = "addViewer" - updateUser.admin = false - updateUser.owner = false + scope = 'addViewer'; + updateUser.admin = false; + updateUser.owner = false; break; } const organizationData = await organizationHelpers(sqlClientPool).getOrganizationByOrganizationInput( organization, scope, - "organization" + 'organization', ); if (organizationData === undefined) { - throw new Error(`Unauthorized: You don't have permission to "${scope}" on "organization"`) + throw new Error(`Unauthorized: You don't have permission to "${scope}" on "organization"`); } const user = await models.UserModel.loadUserByIdOrEmail({ @@ -488,11 +488,11 @@ export const addAdminToOrganization: ResolverFn = async ( email: R.prop('email', userInput), }); - updateUser.id = user.id - updateUser.organization = organizationData.id + updateUser.id = user.id; + updateUser.organization = organizationData.id; await hasPermission('organization', scope, { - organization: organizationData.id + organization: organizationData.id, }); await models.UserModel.updateUser(updateUser); @@ -511,14 +511,13 @@ export const addAdminToOrganization: ResolverFn = async ( organizationId: organizationData.id, }; - // var emailDetails = await getUserOrgEmailDetails(models, user, organizationData, role); - var emailDetails = await getUserOrgEmailDetails('api:addAdminToOrganization', models, user, { - Name: user.email, - Email: user.email, - OrganizationName: organizationData.name, - Role: role, - }); + const emailDetails = await getUserOrgEmailDetails('api:addAdminToOrganization', models, user, { + Name: user.email, + Email: user.email, + OrganizationName: organizationData.name, + Role: role, + }); userActivityLogger(`User added an administrator to organization '${organizationData.name}'`, { project: '', @@ -528,11 +527,11 @@ export const addAdminToOrganization: ResolverFn = async ( id: user.id, email: user.email, organization: organizationData.id, - role: role, + role, }, ...auditLog, ...emailDetails, - } + }, }); return organizationData; @@ -542,17 +541,18 @@ export const addAdminToOrganization: ResolverFn = async ( export const removeAdminFromOrganization: ResolverFn = async ( _root, { input: { user: userInput, organization } }, - { sqlClientPool, models, hasPermission, userActivityLogger }, + { + sqlClientPool, models, hasPermission, userActivityLogger, + }, ) => { - - const scope = 'addOwner' + const scope = 'addOwner'; const organizationData = await organizationHelpers(sqlClientPool).getOrganizationByOrganizationInput( organization, scope, - "organization" + 'organization', ); if (organizationData === undefined) { - throw new Error(`Unauthorized: You don't have permission to scope on "organization"`) + throw new Error('Unauthorized: You don\'t have permission to scope on "organization"'); } const user = await models.UserModel.loadUserByIdOrEmail({ @@ -561,7 +561,7 @@ export const removeAdminFromOrganization: ResolverFn = async ( }); await hasPermission('organization', scope, { - organization: organizationData.id + organization: organizationData.id, }); await models.UserModel.updateUser({ @@ -574,21 +574,21 @@ export const removeAdminFromOrganization: ResolverFn = async ( resource: { id: organizationData.id.toString(), type: AuditType.ORGANIZATION, - details: organizationData.name + details: organizationData.name, }, linkedResource: { id: user.id, type: AuditType.USER, - details: user.email + details: user.email, }, organizationId: organizationData.id, }; - var emailDetails = await getUserOrgEmailDetails('api:removeAdminFromOrganization', models, user, { - Name: user.email, - Email: user.email, - OrganizationName: organizationData.name, - }); + const emailDetails = await getUserOrgEmailDetails('api:removeAdminFromOrganization', models, user, { + Name: user.email, + Email: user.email, + OrganizationName: organizationData.name, + }); userActivityLogger(`User removed an administrator from organization '${organizationData.name}'`, { project: '', @@ -601,7 +601,7 @@ export const removeAdminFromOrganization: ResolverFn = async ( }, ...auditLog, ...emailDetails, - } + }, }); return organizationData; @@ -610,32 +610,28 @@ export const removeAdminFromOrganization: ResolverFn = async ( // query to list all platform users export const getAllPlatformUsers: ResolverFn = async ( _root, - { id, email, gitlabId, role }, + { + id, email, gitlabId, role, + }, { models, adminScopes }, ) => { // if user is platform owner or viewer if (adminScopes.platformOwner || adminScopes.platformViewer) { const users = await models.UserModel.loadAllPlatformUsers(); if (id) { - const filteredById = users.filter(function (item) { - return item.id === id; - }); + const filteredById = users.filter((item) => item.id === id); return filteredById; } if (email) { - const filteredByEmail = users.filter(function (item) { - return item.email === email; - }); + const filteredByEmail = users.filter((item) => item.email === email); return filteredByEmail; } if (gitlabId) { - const filteredByGitlab = users.filter(function (item) { - return item.gitlabId === gitlabId; - }); + const filteredByGitlab = users.filter((item) => item.gitlabId === gitlabId); return filteredByGitlab; } if (role) { - const filteredByPlatformRole = users.filter(function (item) { + const filteredByPlatformRole = users.filter((item) => { const found = item.platformRoles.some(el => el === role); if (found) { return item; @@ -644,11 +640,10 @@ export const getAllPlatformUsers: ResolverFn = async ( return filteredByPlatformRole; } return users; - } else { - throw new Error( - `Unauthorized: You don't have permission to perform this action` - ); } + throw new Error( + 'Unauthorized: You don\'t have permission to perform this action', + ); }; // addPlatformRoleToUser is used to add platform-owner or platform-viewer to a user @@ -665,20 +660,17 @@ export const addPlatformRoleToUser: ResolverFn = async ( }); await models.UserModel.addPlatformRoleToUser(user, role); const users = await models.UserModel.loadAllPlatformUsers(); - const filteredByEmail = users.filter(function (item) { - return item.email === user.email; - }); + const filteredByEmail = users.filter((item) => item.email === user.email); const auditLog: AuditLog = { resource: { id: user.id, type: AuditType.USER, - details: `${user.email} role: ${role}` + details: `${user.email} role: ${role}`, }, }; - - var emailDetails = await getUserOrgEmailDetails('api:addPlatformRoleToUser', models, user, { + const emailDetails = await getUserOrgEmailDetails('api:addPlatformRoleToUser', models, user, { Name: user.email, Email: user.email, Role: role, @@ -691,18 +683,17 @@ export const addPlatformRoleToUser: ResolverFn = async ( user: { id: user.id, email: user.email, - role: role, + role, }, ...auditLog, ...emailDetails, - } + }, }); return filteredByEmail[0]; - } else { - throw new Error( - `Unauthorized: You don't have permission to perform this action` - ); } + throw new Error( + 'Unauthorized: You don\'t have permission to perform this action', + ); }; // removePlatformRoleFromUser will remove a platform role from a user @@ -719,19 +710,17 @@ export const removePlatformRoleFromUser: ResolverFn = async ( }); await models.UserModel.removePlatformRoleFromUser(user, role); const users = await models.UserModel.loadAllPlatformUsers(); - const filteredByEmail = users.filter(function (item) { - return item.email === user.email; - }); + const filteredByEmail = users.filter((item) => item.email === user.email); const auditLog: AuditLog = { resource: { id: user.id, type: AuditType.USER, - details: `${user.email} role: ${role}` + details: `${user.email} role: ${role}`, }, }; - var emailDetails = await getUserOrgEmailDetails('api:removePlatformRoleFromUser', models, user, { + const emailDetails = await getUserOrgEmailDetails('api:removePlatformRoleFromUser', models, user, { Name: user.email, Email: user.email, Role: role, @@ -744,34 +733,32 @@ export const removePlatformRoleFromUser: ResolverFn = async ( user: { id: user.id, email: user.email, - role: role, + role, }, ...auditLog, ...emailDetails, - } + }, }); if (filteredByEmail[0]) { - return filteredByEmail[0] + return filteredByEmail[0]; } return user; - } else { - throw new Error( - `Unauthorized: You don't have permission to perform this action` - ); } + throw new Error( + 'Unauthorized: You don\'t have permission to perform this action', + ); }; - async function getUserOrgEmailDetails(action, models: { UserModel: UserModel; }, user: User, payloadData) { - var emailDetails = {}; + let emailDetails = {}; try { - let dbUserDetails = await models.UserModel.getFullUserDetails(user); - if (dbUserDetails && dbUserDetails['optEmailOrgRole'] == true) { + const dbUserDetails = await models.UserModel.getFullUserDetails(user) as any; + if (dbUserDetails && dbUserDetails.optEmailOrgRole === true) { emailDetails = { userActionEmailDetails: { Name: user.email, Email: user.email, - ...payloadData + ...payloadData, }, }; } @@ -782,7 +769,7 @@ async function getUserOrgEmailDetails(action, models: { UserModel: UserModel; }, user.id, action, payloadData, - `Error while trying to get full user DB details for user(id|email) (${user.id}|${user.email}): error: ${e.message}` + `Error while trying to get full user DB details for user(id|email) (${user.id}|${user.email}): error: ${e.message}`, ); } return emailDetails; @@ -791,16 +778,16 @@ async function getUserOrgEmailDetails(action, models: { UserModel: UserModel; }, export const getUser2fa: ResolverFn = async ( user, _, - { models } + { models }, ) => { - if (user.isFederatedUser !== undefined && user.isFederatedUser === true ) { + if (user.isFederatedUser !== undefined && user.isFederatedUser === true) { // if a federated user sets up 2fa or passkeys in their account after they have logged in // it will never be used unless the user unlinks themselves from the identity provider // if that happens, then the user will have no federated user and their 2fa status will show correctly // if a user is federated though, then their 2fa status is managed/enforced in the external provider // and lagoon has no way to verify if 2fa is enabled or enforced in that provider so just set that the user // is a federated user - return false + return false; } - return models.UserModel.fetchUserTwoFactor(user) -}; \ No newline at end of file + return models.UserModel.fetchUserTwoFactor(user); +}; diff --git a/services/api/src/resources/user/sql.ts b/services/api/src/resources/user/sql.ts index c3fe584649..3780b5b33f 100644 --- a/services/api/src/resources/user/sql.ts +++ b/services/api/src/resources/user/sql.ts @@ -1,72 +1,62 @@ -import { update } from 'ramda'; import { knex } from '../../util/db'; export const Sql = { - selectUserById: (id: string) => - knex('user') - .select('usid', 'opt_email_org_role', 'opt_email_sshkey', 'opt_email_group_role', 'last_accessed') - .where('usid', id) - .toString(), + selectUserById: (id: string) => knex('user') + .select('usid', 'opt_email_org_role', 'opt_email_sshkey', 'opt_email_group_role', 'last_accessed') + .where('usid', id) + .toString(), selectUserIdBySshKey: ({ keyValue, keyType, }: { keyValue: string; keyType: string; - }): string => - knex('user_ssh_key') - .join('ssh_key as sk', 'sk.id', '=', 'user_ssh_key.skid') - .where('sk.key_value', keyValue) - .andWhere('sk.key_type', keyType) - .select('user_ssh_key.usid') - .toString(), - deleteFromSshKeys: (id: string) => - knex('ssh_key') - .whereIn('id', function() { - this.select('skid') - .from('user_ssh_key') - .where('usid', id); - }) - .delete() - .toString(), - deleteFromUserSshKeys: (id: string) => - knex('user_ssh_key') - .where('usid', id) - .delete() - .toString(), + }): string => knex('user_ssh_key') + .join('ssh_key as sk', 'sk.id', '=', 'user_ssh_key.skid') + .where('sk.key_value', keyValue) + .andWhere('sk.key_type', keyType) + .select('user_ssh_key.usid') + .toString(), + deleteFromSshKeys: (id: string) => knex('ssh_key') + .whereIn('id', function () { + this.select('skid') + .from('user_ssh_key') + .where('usid', id); + }) + .delete() + .toString(), + deleteFromUserSshKeys: (id: string) => knex('user_ssh_key') + .where('usid', id) + .delete() + .toString(), selectUserIdBySshFingerprint: ({ keyFingerprint, }: { keyFingerprint: string; - }): string => - knex('user_ssh_key') - .join('ssh_key as sk', 'sk.id', '=', 'user_ssh_key.skid') - .where('sk.key_fingerprint', keyFingerprint) - .select('user_ssh_key.usid') - .toString(), - updateLastAccessed: (id: string) => - knex('user') - .insert({ - usid: id, - lastAccessed: knex.fn.now(), - }) - .onConflict('usid') - .merge() - .toString(), - updateUserDBTable: (id: string, data: Record) => - knex('user') - .insert({usid: id, ...data}) - .onConflict('usid') - .merge({...data}) - .toString(), - selectLastAccessed: (id: string) => - knex('user') - .select('last_accessed') - .where('usid','=',id) - .toString(), - deleteFromUser: (id: string) => - knex('user') - .where('usid', id) - .delete() - .toString(), + }): string => knex('user_ssh_key') + .join('ssh_key as sk', 'sk.id', '=', 'user_ssh_key.skid') + .where('sk.key_fingerprint', keyFingerprint) + .select('user_ssh_key.usid') + .toString(), + updateLastAccessed: (id: string) => knex('user') + .insert({ + usid: id, + lastAccessed: knex.fn.now(), + }) + .onConflict('usid') + .merge() + .toString(), + updateUserDBTable: (id: string, data: Record) => knex('user') + .insert({ usid: id, ...data }) + .onConflict('usid') + .merge({ ...data }) + .toString(), + selectLastAccessed: (id: string) => knex('user') + .select('last_accessed') + .where('usid', '=', id) + .toString(), + deleteFromUser: (id: string) => knex('user') + .where('usid', id) + .delete() + .toString(), }; diff --git a/services/api/src/routes/index.ts b/services/api/src/routes/index.ts index fe14c1c1e4..4d2031d71e 100644 --- a/services/api/src/routes/index.ts +++ b/services/api/src/routes/index.ts @@ -7,8 +7,7 @@ export function createRouter() { const router = express.Router(); // Redirect GET requests on "/" to the status route. - router.get('/', (req: Request, res: Response) => - res.redirect('/status'), + router.get('/', (req: Request, res: Response) => res.redirect('/status'), ); // Fetch the current api status. diff --git a/services/api/src/routes/keys.ts b/services/api/src/routes/keys.ts index c805898cb8..c0e69a3d36 100644 --- a/services/api/src/routes/keys.ts +++ b/services/api/src/routes/keys.ts @@ -1,6 +1,6 @@ import * as R from 'ramda'; import bodyParser from 'body-parser'; -import { Request, Response } from 'express'; +import { Response } from 'express'; import { RequestWithAuthData } from '../authMiddleware'; import { logger } from '../loggers/logger'; import { knex, query } from '../util/db'; @@ -9,13 +9,12 @@ import { validateKey } from '../util/func'; const toFingerprint = async (sshKey) => { try { - const pubkey = await validateKey(sshKey, "public") - if (pubkey['sha256fingerprint']) { - return pubkey['sha256fingerprint'] - } else { - throw new Error('not valid key') + const pubkey = await validateKey(sshKey, 'public'); + if (pubkey.sha256fingerprint) { + return pubkey.sha256fingerprint; } - } catch (e) { + throw new Error('not valid key'); + } catch (_e) { logger.error(`Invalid ssh key: ${sshKey}`); } }; @@ -23,10 +22,10 @@ const toFingerprint = async (sshKey) => { const mapFingerprints = async (keys) => { const fingerprintKeyMap = await Promise.all( keys.map(async sshKey => { - const fp = await toFingerprint(sshKey) - return {fingerprint: fp, key: sshKey} - })) - return fingerprintKeyMap + const fp = await toFingerprint(sshKey); + return { fingerprint: fp, key: sshKey }; + })); + return fingerprintKeyMap; }; const keysRoute = async ( @@ -47,13 +46,13 @@ const keysRoute = async ( sqlClientPool, knex('ssh_key AS sk') .select(knex.raw("CONCAT(sk.key_type, ' ', sk.key_value) as sshKey")) - .where("key_fingerprint","=", fingerprint) + .where('key_fingerprint', '=', fingerprint) .toString(), ); const keys = R.map(R.prop('sshKey'), rows); - const fingerprintKeyMap = await mapFingerprints(keys) - const found = await fingerprintKeyMap.filter(el => {if (el.fingerprint === fingerprint) { return el.key }})[0]; + const fingerprintKeyMap = await mapFingerprints(keys); + const found = await fingerprintKeyMap.filter(el => { if (el.fingerprint === fingerprint) { return el.key; } })[0]; if (!found) { logger.debug(`Unknown fingerprint: ${fingerprint}`); @@ -70,14 +69,14 @@ const keysRoute = async ( ); // check if a key is found if (foundkey.length > 0) { - var date = new Date(); + const date = new Date(); const convertDateFormat = R.init; - var lastUsed = convertDateFormat(date.toISOString()); + const lastUsed = convertDateFormat(date.toISOString()); await query( sqlClientPool, knex('ssh_key') .where('id', foundkey[0].id) - .update({lastUsed: lastUsed}) + .update({ lastUsed }) .toString(), ); } diff --git a/services/api/src/routes/well-known.ts b/services/api/src/routes/well-known.ts index cdfc3af2b5..5c7803b162 100644 --- a/services/api/src/routes/well-known.ts +++ b/services/api/src/routes/well-known.ts @@ -1,17 +1,17 @@ import { Request, Response } from 'express'; -import { envHasConfig, getConfigFromEnv } from '../util/config'; +import { getConfigFromEnv } from '../util/config'; const wellKnown = (req: Request, res: Response) => { - let discoverData = { + const discoverData = { lagoon_version: getConfigFromEnv('LAGOON_VERSION', 'unknown'), authorization_endpoint: getConfigFromEnv('KEYCLOAK_FRONTEND_URL', ''), ssh_token_exchange: { token_endpoint_host: getConfigFromEnv('SSH_TOKEN_ENDPOINT', ''), - token_endpoint_port: parseInt(getConfigFromEnv('SSH_TOKEN_ENDPOINT_PORT', '22'), 10) + token_endpoint_port: parseInt(getConfigFromEnv('SSH_TOKEN_ENDPOINT_PORT', '22'), 10), }, webhook_endpoint: getConfigFromEnv('WEBHOOK_URL', ''), - ui_url: getConfigFromEnv('UI_URL',''), - } + ui_url: getConfigFromEnv('UI_URL', ''), + }; res.json(discoverData); }; diff --git a/services/api/src/server.ts b/services/api/src/server.ts index 42adaa5f2a..0181476a00 100644 --- a/services/api/src/server.ts +++ b/services/api/src/server.ts @@ -1,5 +1,6 @@ import http from 'http'; import util from 'util'; +import { AuthenticationError } from 'apollo-server-express'; import { logger } from './loggers/logger'; import { toNumber } from './util/func'; import { getConfigFromEnv } from './util/config'; @@ -13,11 +14,33 @@ import { Group } from './models/group'; import { Environment } from './models/environment'; import { keycloakGrantManager } from './clients/keycloakClient'; import { keycloakHasPermission, legacyHasPermission } from './util/auth'; -import R from 'ramda'; -import { AuthenticationError } from 'apollo-server-express'; + const { useServer } = require('graphql-ws/lib/use/ws'); const { createHandler } = require('graphql-sse/lib/use/http'); +/** + * Extract a bearer token from graphql-ws connectionParams. + * The lagoon-beta-ui sends the token in different shapes depending on whether + * the connection is made server-side (apolloClient.ts) or client-side + * (apollo-client-components.tsx): + * + * Server-side: { Authorization: "Bearer " } (top-level) + * Client-side: { headers: { authorization: "Bearer " } } (nested) + * + * We also keep backward-compat for `authToken` (plain token, no Bearer prefix). + */ +function extractWsToken(connectionParams: Record | null | undefined): string | null { + const p = connectionParams || {}; + const stripBearer = (v: string) => v.replace(/^bearer\s+/i, '').trim(); + + if (p.authToken) return p.authToken; + if (p.Authorization) return stripBearer(p.Authorization); + if (p.authorization) return stripBearer(p.authorization); + if (p.headers?.Authorization) return stripBearer(p.headers.Authorization); + if (p.headers?.authorization) return stripBearer(p.headers.authorization); + return null; +} + export const createServer = async () => { logger.verbose('Starting the api...'); @@ -28,7 +51,7 @@ export const createServer = async () => { const schema = await getSchema(); const sseHandler = createHandler({ - schema: schema, + schema, authenticate: async (req) => { const authHeader = req.raw.headers.authorization || req.raw.headers.Authorization; @@ -47,7 +70,7 @@ export const createServer = async () => { throw new AuthenticationError('Auth token invalid.'); } }, - context: async (req, params) => { + context: async (req, _params) => { const { grant: keycloakGrant, legacyCredentials: legacyGrant } = req.context || {}; const keycloakAdminClient = await getKeycloakAdminClient(); @@ -87,14 +110,14 @@ export const createServer = async () => { const hasPermission = keycloakGrant ? keycloakHasPermission(keycloakGrant, modelClients, serviceAccount, currentUser, groupRoleProjectIds) : legacyGrant - ? legacyHasPermission(legacyGrant) - : () => false; + ? legacyHasPermission(legacyGrant) + : () => false; logger.debug('SSE: Created context', { hasKeycloakGrant: !!keycloakGrant, hasLegacyGrant: !!legacyGrant, platformOwner, - platformViewer + platformViewer, }); return { @@ -106,7 +129,7 @@ export const createServer = async () => { models: { UserModel: User(modelClients), GroupModel: Group(modelClients), - EnvironmentModel: Environment(modelClients) + EnvironmentModel: Environment(modelClients), }, keycloakUsersGroups, adminScopes: { platformOwner, platformViewer }, @@ -116,20 +139,20 @@ export const createServer = async () => { logger.debug('SSE onSubscribe', { operationName: params?.operationName, query: params?.query, - variables: params?.variables + variables: params?.variables, }); }, onNext: (req, params, result) => { logger.debug('SSE onNext', { hasResult: !!result, resultData: result?.data, - errors: result?.errors + errors: result?.errors, }); }, onError: (req, params, errors) => { logger.error('SSE onError', { - errors: errors, - params: params + errors, + params, }); }, onComplete: (req, params) => { @@ -149,7 +172,7 @@ export const createServer = async () => { } catch (error: any) { logger.error('SSE handler error', { error: error.message, - stack: error.stack + stack: error.stack, }); if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'application/json' }); @@ -163,198 +186,126 @@ export const createServer = async () => { server.setTimeout(900000); async function setupGraphQLModules() { -try { - const WebSocket = (await import("ws")).default; - const schema = await getSchema(); - - const wsServer = new WebSocket.Server({ - server, - path: "/graphql", - }); + try { + const WebSocket = (await import('ws')).default; + const schema = await getSchema(); - useServer( - { - schema: schema, - context: async (ctx) => { - const connectionParams = ctx.connectionParams || {}; - let keycloakGrant = null; - let legacyGrant = null; - - try { - if (connectionParams.authToken) { - const { grant, legacyCredentials } = await getGrantOrLegacyCredsFromToken( - connectionParams.authToken, - ); - keycloakGrant = grant; - legacyGrant = legacyCredentials; - } - } catch (e) { - logger.error(`WebSocket auth failed: ${e.message}`); - } + const wsServer = new WebSocket.Server({ + server, + path: '/graphql', + }); - const keycloakAdminClient = await getKeycloakAdminClient(); - const modelClients = { sqlClientPool, keycloakAdminClient, esClient }; - - let currentUser: any = { id: undefined }; - let serviceAccount = {}; - let keycloakUsersGroups = []; - let groupRoleProjectIds = []; - let platformOwner = false; - let platformViewer = false; - - if (keycloakGrant) { - try { - const userId = keycloakGrant.access_token.content.sub; - keycloakUsersGroups = await User(modelClients).getAllGroupsForUser(userId); - serviceAccount = await keycloakGrantManager.obtainFromClientCredentials(undefined, undefined); - currentUser = await User(modelClients).loadUserById(userId); - const userRoleMapping = await keycloakAdminClient.users.listRealmRoleMappings({ id: currentUser.id }); - platformOwner = userRoleMapping.some(role => role.name === 'platform-owner'); - platformViewer = userRoleMapping.some(role => role.name === 'platform-viewer'); - groupRoleProjectIds = await User(modelClients).getAllProjectsIdsForUser(currentUser.id, keycloakUsersGroups); - await User(modelClients).userLastAccessed(currentUser); - } catch (e) { - logger.error('Error loading user details for subscription', e.message); - throw new AuthenticationError('Failed to load user context for subscription'); - } - } + useServer( + { + schema, + context: async (ctx) => { + const token = extractWsToken(ctx.connectionParams); + let keycloakGrant = null; + let legacyGrant = null; - if (legacyGrant) { - const { role } = legacyGrant; - if (role === 'admin') { - platformOwner = true; - } - } + try { + if (token) { + const { grant, legacyCredentials } = await getGrantOrLegacyCredsFromToken(token); + keycloakGrant = grant; + legacyGrant = legacyCredentials; + } + } catch (e) { + logger.error(`WebSocket auth failed: ${e.message}`); + } - const hasPermission = keycloakGrant - ? keycloakHasPermission(keycloakGrant, modelClients, serviceAccount, currentUser, groupRoleProjectIds) - : legacyHasPermission(legacyGrant); - - return { - keycloakAdminClient, - sqlClientPool, - hasPermission, - keycloakGrant, - legacyGrant, - models: { - UserModel: User(modelClients), - GroupModel: Group(modelClients), - EnvironmentModel: Environment(modelClients) - }, - keycloakUsersGroups, - adminScopes: { platformOwner, platformViewer }, - }; - }, - onConnect: async (ctx) => { - const token = R.prop('authToken', ctx.connectionParams); - if (!token) { - logger.error('WebSocket onConnect: No auth token found in connection parameters'); - throw new AuthenticationError('Auth token missing.'); - } + const keycloakAdminClient = await getKeycloakAdminClient(); + const modelClients = { sqlClientPool, keycloakAdminClient, esClient }; + + let currentUser: any = { id: undefined }; + let serviceAccount = {}; + let keycloakUsersGroups = []; + let groupRoleProjectIds = []; + let platformOwner = false; + let platformViewer = false; + + if (keycloakGrant) { + try { + const userId = keycloakGrant.access_token.content.sub; + keycloakUsersGroups = await User(modelClients).getAllGroupsForUser(userId); + serviceAccount = await keycloakGrantManager.obtainFromClientCredentials(undefined, undefined); + currentUser = await User(modelClients).loadUserById(userId); + const userRoleMapping = await keycloakAdminClient.users.listRealmRoleMappings({ id: currentUser.id }); + platformOwner = userRoleMapping.some(role => role.name === 'platform-owner'); + platformViewer = userRoleMapping.some(role => role.name === 'platform-viewer'); + groupRoleProjectIds = await User(modelClients).getAllProjectsIdsForUser(currentUser.id, keycloakUsersGroups); + await User(modelClients).userLastAccessed(currentUser); + } catch (e) { + logger.error('Error loading user details for subscription', e.message); + throw new AuthenticationError('Failed to load user context for subscription'); + } + } - try { - const { grant, legacyCredentials } = await getGrantOrLegacyCredsFromToken(token); - const keycloakAdminClient = await getKeycloakAdminClient(); - const modelClients = { sqlClientPool, keycloakAdminClient, esClient }; - - let currentUser: any = {}; - let serviceAccount = {}; - let keycloakUsersGroups = []; - let groupRoleProjectIds: any[] = []; - const keycloakGrant = grant; - let legacyGrant = legacyCredentials ? legacyCredentials : null; - let platformOwner = false; - let platformViewer = false; - - if (keycloakGrant) { - try { - keycloakUsersGroups = await User(modelClients).getAllGroupsForUser(keycloakGrant.access_token.content.sub); - serviceAccount = await keycloakGrantManager.obtainFromClientCredentials(undefined, undefined); - currentUser = await User(modelClients).loadUserById(keycloakGrant.access_token.content.sub); - const userRoleMapping = await keycloakAdminClient.users.listRealmRoleMappings({id: currentUser.id}) - for (const role of userRoleMapping) { - if (role.name == "platform-owner") { - platformOwner = true - } - if (role.name == "platform-viewer") { - platformViewer = true - } + if (legacyGrant) { + const { role } = legacyGrant; + if (role === 'admin') { + platformOwner = true; } - groupRoleProjectIds = await User(modelClients).getAllProjectsIdsForUser(currentUser.id, keycloakUsersGroups); - } catch (err) { - logger.error('WebSocket onConnect: Error loading user context.', { error: err.message, userId: keycloakGrant.access_token.content.sub }); - throw err; } - } - if (legacyGrant) { - const { role } = legacyGrant; - if (role == 'admin') { - platformOwner = true + + const hasPermission = keycloakGrant + ? keycloakHasPermission(keycloakGrant, modelClients, serviceAccount, currentUser, groupRoleProjectIds) + : legacyGrant + ? legacyHasPermission(legacyGrant) + : () => Promise.reject(new Error('Unauthorized')); + + return { + keycloakAdminClient, + sqlClientPool, + hasPermission, + keycloakGrant, + legacyGrant, + models: { + UserModel: User(modelClients), + GroupModel: Group(modelClients), + EnvironmentModel: Environment(modelClients), + }, + keycloakUsersGroups, + adminScopes: { platformOwner, platformViewer }, + }; + }, + onConnect: async (ctx) => { + const token = extractWsToken(ctx.connectionParams); + if (!token) { + logger.debug('WebSocket onConnect: No auth token in connection params; auth will be checked per-operation.'); } - } - - const context = { - keycloakAdminClient, - sqlClientPool, - hasPermission: grant - ? keycloakHasPermission(grant, modelClients, serviceAccount, currentUser, groupRoleProjectIds) - : legacyHasPermission(legacyCredentials), - keycloakGrant, - legacyGrant, - models: { - UserModel: User(modelClients), - GroupModel: Group(modelClients), - EnvironmentModel: Environment(modelClients) - }, - keycloakUsersGroups, - adminScopes: { platformOwner, platformViewer }, - }; - - logger.debug('WebSocket onConnect: Created context', { - hasKeycloakGrant: !!context.keycloakGrant, - hasLegacyGrant: !!context.legacyGrant, - platformOwner: context.adminScopes.platformOwner, - platformViewer: context.adminScopes.platformViewer - }); - - return context; - } catch (err) { - logger.error('WebSocket onConnect: Authentication error.', { error: err.message, stack: err.stack }); - throw err; - } - }, - onSubscribe: async (ctx, msg) => { - logger.debug('WebSocket onSubscribe', { - messageType: msg.type, - operationName: msg.payload?.operationName, - query: msg.payload?.query - }); - return undefined; - }, - onNext: (ctx, msg, args, result) => { - logger.debug('WebSocket onNext', { - messageType: msg.type, - hasResult: !!result, - resultData: result?.data - }); - return result; - }, - onError: (ctx, msg, errors) => { - logger.error('WebSocket onError', { errors }); - }, - onDisconnect: (ctx: any, code, reason) => { - logger.verbose('WebSocket disconnected'); - }, - }, - wsServer - ); - logger.info(`Subscription endpoint ready at ws://localhost:${port}/graphql`); - logger.info(`SSE subscription endpoint ready at http://localhost:${port}/graphql/stream`); - -} catch (error) { - logger.error("Failed to load GraphQL modules:", error); - throw error; -} + return true; + }, + onSubscribe: async (ctx, msg) => { + logger.debug('WebSocket onSubscribe', { + messageType: msg.type, + operationName: msg.payload?.operationName, + query: msg.payload?.query, + }); + return undefined; + }, + onNext: (ctx, msg, args, result) => { + logger.debug('WebSocket onNext', { + messageType: msg.type, + hasResult: !!result, + resultData: result?.data, + }); + return result; + }, + onError: (ctx, msg, errors) => { + logger.error('WebSocket onError', { errors }); + }, + onDisconnect: (_ctx: any, _code, _reason) => { + logger.verbose('WebSocket disconnected'); + }, + }, + wsServer, + ); + logger.info(`Subscription endpoint ready at ws://localhost:${port}/graphql`); + logger.info(`SSE subscription endpoint ready at http://localhost:${port}/graphql/stream`); + } catch (error) { + logger.error('Failed to load GraphQL modules:', error); + throw error; + } } await setupGraphQLModules(); diff --git a/services/api/src/util/auth.ts b/services/api/src/util/auth.ts index 082ab3db9d..100fc756a3 100644 --- a/services/api/src/util/auth.ts +++ b/services/api/src/util/auth.ts @@ -1,13 +1,15 @@ import * as R from 'ramda'; import { verify } from 'jsonwebtoken'; import { logger } from '../loggers/logger'; -import { getConfigFromEnv } from '../util/config'; +import { getConfigFromEnv } from './config'; import { encodeJSONBase64, isNotNil } from './func'; import { keycloakGrantManager } from '../clients/keycloakClient'; -const { userActivityLogger } = require('../loggers/userActivityLogger'); + import { Group } from '../models/group'; import { User } from '../models/user'; +const { userActivityLogger } = require('../loggers/userActivityLogger'); + export interface LegacyToken { iat: number; exp: number; @@ -30,12 +32,12 @@ const sortRolesByWeight = (a, b) => { reporter: 2, developer: 3, maintainer: 4, - owner: 5 + owner: 5, }; if (roleWeights[a] < roleWeights[b]) { return -1; - } else if (roleWeights[a] > roleWeights[b]) { + } if (roleWeights[a] > roleWeights[b]) { return 1; } @@ -43,32 +45,32 @@ const sortRolesByWeight = (a, b) => { }; export const getUserProjectIdsFromRoleProjectIds = ( - roleProjectIds + roleProjectIds, ): number[] => { // https://github.com/uselagoon/lagoon/pull/3358 references potential issue with the lagoon-projects attribute where there could be empty values // the structure of this payload is: /* {"guest":[13],"devloper":[20,14],"maintainer":[13,18,19,12]} */ - let upids = []; + const upids = []; for (const r in roleProjectIds) { for (const pid in roleProjectIds[r]) { - upids.indexOf(roleProjectIds[r][pid]) === -1 ? upids.push(roleProjectIds[r][pid]) : "" + upids.indexOf(roleProjectIds[r][pid]) === -1 ? upids.push(roleProjectIds[r][pid]) : ''; } } return R.uniq(upids); }; export const getUserRoleForProjectFromRoleProjectIds = ( - roleProjectIds, projectId + roleProjectIds, projectId, ): [string, number[]] => { - let upids = []; - let roles = []; + const upids = []; + const roles = []; for (const r in roleProjectIds) { for (const pid in roleProjectIds[r]) { - upids.indexOf(roleProjectIds[r][pid]) === -1 ? upids.push(roleProjectIds[r][pid]) : "" - if (projectId == roleProjectIds[r][pid]) { - roles.push(r) + upids.indexOf(roleProjectIds[r][pid]) === -1 ? upids.push(roleProjectIds[r][pid]) : ''; + if (projectId === roleProjectIds[r][pid]) { + roles.push(r); } } } @@ -78,15 +80,13 @@ export const getUserRoleForProjectFromRoleProjectIds = ( return [highestRoleForProject, R.uniq(upids)]; }; -const getHighestRole = (roles) => { - return R.pipe( - R.uniq, - R.reject(R.isEmpty), - R.reject(R.isNil), - R.sort(sortRolesByWeight), - R.last - )(roles); -}; +const getHighestRole = (roles): string => R.pipe( + R.uniq, + R.reject(R.isEmpty), + R.reject(R.isNil), + R.sort(sortRolesByWeight), + R.last, +)(roles) as string; export const isLegacyToken = R.pathSatisfies(isNotNil, ['payload', 'role']); export const isKeycloakToken = R.pathSatisfies(isNotNil, ['payload', 'typ']); @@ -94,18 +94,18 @@ export const isKeycloakToken = R.pathSatisfies(isNotNil, ['payload', 'typ']); export const getGrantForKeycloakToken = async (token: string) => { // Create grant, validating token is signed/fresh. const grant = await keycloakGrantManager.createGrant({ - access_token: token + access_token: token, }); // Validates token has a live session. const valid = await keycloakGrantManager.validateAccessToken(grant.access_token, undefined); if (!valid) { - throw new Error('session signed out') + throw new Error('session signed out'); } - return grant -} + return grant; +}; export const getCredentialsForLegacyToken = async (token: string): Promise => { const decoded = verify(token, getConfigFromEnv('JWTSECRET')); @@ -114,23 +114,25 @@ export const getCredentialsForLegacyToken = async (token: string): Promise parseInt(maxExpiry)) { - const msg = `Legacy token (sub:${sub}; iss:${iss}) expiry ${(exp-iat)} is greater than ${parseInt(maxExpiry)}` + if ((exp - iat) > parseInt(maxExpiry)) { + const msg = `Legacy token (sub:${sub}; iss:${iss}) expiry ${(exp - iat)} is greater than ${parseInt(maxExpiry)}`; logger.warn(msg); - if (rejectLegacyExpiry == "true") { + if (rejectLegacyExpiry === 'true') { throw new Error(msg); } } } else { - const msg = `Legacy token (sub:${sub}; iss:${iss}) has no expiry` + const msg = `Legacy token (sub:${sub}; iss:${iss}) has no expiry`; logger.warn(msg); - if (rejectLegacyExpiry == "true") { + if (rejectLegacyExpiry === 'true') { throw new Error(msg); } } @@ -150,7 +152,7 @@ export const getCredentialsForLegacyToken = async (token: string): Promise { userActivityLogger.user_info( `User does not have permission to '${scope}' on '${resource}'`, { - user: legacyCredentials ? legacyCredentials : null - } + user: legacyCredentials || null, + }, ); throw new Error('Unauthorized'); } @@ -183,7 +185,6 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre const UserModel = User(modelClients); return async (resource, scope, attributes: IKeycloakAuthAttributes = {}) => { - let claims: { currentUser: [string]; usersQuery?: [string]; @@ -196,7 +197,7 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre userOrganizationsAdmin?: [string]; userOrganizationsView?: [string]; } = { - currentUser: [currentUser.id] + currentUser: [currentUser.id], }; const usersAttribute = R.prop('users', attributes); @@ -206,10 +207,10 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre usersQuery: [ R.compose( R.join('|'), - R.prop('users') - )(attributes) + R.prop('users'), + )(attributes), ], - currentUser: [currentUser.id] + currentUser: [currentUser.id], }; } @@ -217,28 +218,28 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre if (R.prop('lagoon-organizations', currentUser.attributes)) { claims = { ...claims, - userOrganizations: [`${R.prop('lagoon-organizations', currentUser.attributes)}`] + userOrganizations: [`${R.prop('lagoon-organizations', currentUser.attributes)}`], }; } // check organization admin attributes if (R.prop('lagoon-organizations-admin', currentUser.attributes)) { claims = { ...claims, - userOrganizationsAdmin: [`${R.prop('lagoon-organizations-admin', currentUser.attributes)}`] + userOrganizationsAdmin: [`${R.prop('lagoon-organizations-admin', currentUser.attributes)}`], }; } // check organization viewer attributes if (R.prop('lagoon-organizations-viewer', currentUser.attributes)) { claims = { ...claims, - userOrganizationsView: [`${R.prop('lagoon-organizations-viewer', currentUser.attributes)}`] + userOrganizationsView: [`${R.prop('lagoon-organizations-viewer', currentUser.attributes)}`], }; } - if (R.prop('organization', attributes)) { - const organizationId = parseInt(R.prop('organization', attributes), 10); + if (R.prop('organization' as any, attributes)) { + const organizationId = parseInt(R.prop('organization' as any, attributes) as string, 10); claims = { ...claims, - organizationQuery: [`${organizationId}`] + organizationQuery: [`${organizationId}`], }; } @@ -250,10 +251,10 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre try { claims = { ...claims, - projectQuery: [`${projectId}`] + projectQuery: [`${projectId}`], }; - let [highestRoleForProject, upids] = getUserRoleForProjectFromRoleProjectIds(groupRoleProjectIds, projectId) + let [highestRoleForProject, upids] = getUserRoleForProjectFromRoleProjectIds(groupRoleProjectIds, projectId); if (!highestRoleForProject) { // if no role is detected, fall back to checking the slow way. this is usually only going to be on project creation @@ -262,25 +263,25 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre // grab the users project ids and roles in the first request groupRoleProjectIds = await UserModel.getAllProjectsIdsForUser(currentUser.id, keycloakUsersGroups); - [highestRoleForProject, upids] = getUserRoleForProjectFromRoleProjectIds(groupRoleProjectIds, projectId) + [highestRoleForProject, upids] = getUserRoleForProjectFromRoleProjectIds(groupRoleProjectIds, projectId); } if (upids.length) { claims = { ...claims, - userProjects: [upids.join('-')] + userProjects: [upids.join('-')], }; } if (highestRoleForProject) { claims = { ...claims, - userProjectRole: [highestRoleForProject] + userProjectRole: [highestRoleForProject], }; } } catch (err) { logger.error( - `Couldn't submit project (${projectId}) claims for authz request: ${err.message}` + `Couldn't submit project (${projectId}) claims for authz request: ${err.message}`, ); } } @@ -288,16 +289,15 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre if (R.prop('group', attributes)) { try { const group = await GroupModel.loadGroupById( - R.prop('group', attributes) + R.prop('group', attributes), ); - const groupMembers = await GroupModel.getGroupMembership(group) + const groupMembers = await GroupModel.getGroupMembership(group); const groupRoles = R.pipe( - R.filter(membership => - R.pathEq(['user', 'id'], currentUser.id, membership) + R.filter(membership => R.pathEq(currentUser.id, ['user', 'id'], membership), ), - R.pluck('role') + R.pluck('role'), )(groupMembers); const highestRoleForGroup = R.pipe( @@ -305,21 +305,21 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre R.reject(R.isEmpty), R.reject(R.isNil), R.sort(sortRolesByWeight), - R.last + R.last, )(groupRoles); if (highestRoleForGroup) { claims = { ...claims, - userGroupRole: [highestRoleForGroup] + userGroupRole: [highestRoleForGroup as string], }; } } catch (err) { logger.error( `Couldn't submit group (${R.prop( 'group', - attributes - )}) claims for authz request: ${err.message}` + attributes, + )}) claims for authz request: ${err.message}`, ); } } @@ -333,44 +333,44 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre permissions: [ { id: resource, - scopes: [scope] - } - ] + scopes: [scope], + }, + ], }; if (!R.isEmpty(claims)) { authzRequest = { ...authzRequest, claim_token_format: 'urn:ietf:params:oauth:token-type:jwt', - claim_token: encodeJSONBase64(claims) + claim_token: encodeJSONBase64(claims), }; } const request = { headers: {}, kauth: { - grant: serviceAccount - } + grant: serviceAccount, + }, }; try { const newGrant = await keycloakGrantManager.checkPermissions( authzRequest, - request + request, ); if (newGrant.access_token.hasPermission(resource, scope)) { return; } - } catch (err) { + } catch (_err) { // Keycloak library doesn't distinguish between a request error or access // denied conditions. userActivityLogger.user_info( `User does not have permission to '${scope}' on '${resource}'`, { user: currentUser.id, - attributes: attributes - } + attributes, + }, ); } @@ -378,11 +378,11 @@ export const keycloakHasPermission = (grant, modelClients, serviceAccount, curre `User does not have permission to '${scope}' on '${resource}'`, { user: grant.access_token.content, - attributes: attributes - } + attributes, + }, ); throw new KeycloakUnauthorizedError( - `Unauthorized: You don't have permission to "${scope}" on "${resource}"` + `Unauthorized: You don't have permission to "${scope}" on "${resource}"`, ); }; }; diff --git a/services/api/src/util/config.ts b/services/api/src/util/config.ts index 85625c110c..dc582d2f00 100644 --- a/services/api/src/util/config.ts +++ b/services/api/src/util/config.ts @@ -1,4 +1,4 @@ -export * from '@lagoon/commons/dist/util/config'; +export * from '../commons/util/config'; const s3Config = { signedLinkExpiration: process.env.S3_SIGNED_LINK_EXPIRATION @@ -8,4 +8,4 @@ const s3Config = { export { s3Config }; -export { getLagoonRouteFromEnv } from '@lagoon/commons/dist/util/lagoon' +export { getLagoonRouteFromEnv } from '../commons/util/lagoon'; diff --git a/services/api/src/util/convertDateToMYSQLDateTimeFormat.ts b/services/api/src/util/convertDateToMYSQLDateTimeFormat.ts index 5c1f32c697..0b7e0a0ff1 100644 --- a/services/api/src/util/convertDateToMYSQLDateTimeFormat.ts +++ b/services/api/src/util/convertDateToMYSQLDateTimeFormat.ts @@ -14,5 +14,4 @@ export const convertDateToMYSQLDateFormat = (date: string) => { .toString(); }; - -export default convertDateToMYSQLDateTimeFormat; \ No newline at end of file +export default convertDateToMYSQLDateTimeFormat; diff --git a/services/api/src/util/db.ts b/services/api/src/util/db.ts index 40d3ec8a97..95f18844ff 100644 --- a/services/api/src/util/db.ts +++ b/services/api/src/util/db.ts @@ -2,7 +2,7 @@ import camelcaseKeys from 'camelcase-keys'; import snakecaseKeys from 'snakecase-keys'; import * as R from 'ramda'; import { Pool, Connection } from 'mariadb'; -import { notArray } from '../util/func'; +import { notArray } from './func'; import snakecase from './snakeCase'; export const knex = require('knex')({ @@ -14,30 +14,30 @@ export const knex = require('knex')({ parseJsonResponse: response => { if (!response || typeof response !== 'object') { return response; - } else if (Array.isArray(response)) { + } if (Array.isArray(response)) { return R.map(camelcaseKeys, response); } return camelcaseKeys(response); - } + }, }); // set up the lagoon migrations table export const migrate = require('knex')({ client: 'mysql2', connection: { - host : process.env.API_DB_HOST || 'api-db', - port : process.env.API_DB_PORT || 3306, - user : process.env.API_DB_USERNAME || 'api', - password : process.env.API_DB_PASSWORD || 'api', - database : process.env.API_DB_DATABASE || 'infrastructure' + host: process.env.API_DB_HOST || 'api-db', + port: process.env.API_DB_PORT || 3306, + user: process.env.API_DB_USERNAME || 'api', + password: process.env.API_DB_PASSWORD || 'api', + database: process.env.API_DB_DATABASE || 'infrastructure', }, pool: { min: 2, - max: 10 + max: 10, }, migrations: { tableName: 'lagoon_migrations', - directory: 'dist/migrations/lagoon/migrations' + directory: 'dist/migrations/lagoon/migrations', }, }); @@ -67,7 +67,7 @@ export const migrate = require('knex')({ export const query = async ( conn: Pool | Connection, sql: string, - values: Object | any[] = {} + values: object | any[] = {}, ): Promise => { const preparedValues = R.when(notArray, snakecaseKeys); const rows = await conn.query( @@ -75,14 +75,14 @@ export const query = async ( dateStrings: true, namedPlaceholders: notArray(values), rowsAsArray: false, - sql + sql, }, - preparedValues(values) + preparedValues(values), ); return R.length(rows) > 0 ? R.map(camelcaseKeys, rows) : rows; }; export const isPatchEmpty = R.compose( R.isEmpty, - R.propOr({}, 'patch') + R.propOr({}, 'patch'), ); diff --git a/services/api/src/util/func.ts b/services/api/src/util/func.ts index 9dbe3df1c5..3c75ea9948 100644 --- a/services/api/src/util/func.ts +++ b/services/api/src/util/func.ts @@ -1,4 +1,4 @@ -export * from '@lagoon/commons/dist/util/func'; +export * from '../commons/util/func'; // Check if the GraphQL request contains any fields that are not in the provided args export function requestsExtraFields(info, minimalFields: string[]): boolean { @@ -19,4 +19,4 @@ export function requestsExtraFields(info, minimalFields: string[]): boolean { }); return hasNonMinimalField; -} \ No newline at end of file +} diff --git a/services/api/src/util/gql.ts b/services/api/src/util/gql.ts index f7edafe369..e2382fd9dd 100644 --- a/services/api/src/util/gql.ts +++ b/services/api/src/util/gql.ts @@ -15,4 +15,4 @@ export function gql( result += literals[literals.length - 1]; return result; -}; +} diff --git a/services/api/src/util/pickNonNil.ts b/services/api/src/util/pickNonNil.ts index ce7fb6aca3..7afd3f4643 100644 --- a/services/api/src/util/pickNonNil.ts +++ b/services/api/src/util/pickNonNil.ts @@ -5,10 +5,9 @@ import * as R from 'ramda'; const pickNonNil = ( names: ReadonlyArray, obj: { [propName: string]: any }, -): Object => - R.pipe( - R.pick(names), - R.pickBy(R.complement(R.isNil)), - )(obj); +): Record => R.pipe( + R.pick(names), + R.pickBy(R.complement(R.isNil)), +)(obj); export default R.curry(pickNonNil); diff --git a/services/api/src/util/waitForKeycloak.ts b/services/api/src/util/waitForKeycloak.ts index 1ec17abe70..76bff35cda 100644 --- a/services/api/src/util/waitForKeycloak.ts +++ b/services/api/src/util/waitForKeycloak.ts @@ -16,7 +16,7 @@ export const waitForKeycloak = async () => { } const clients = await keycloakAdminClient.clients.find({ - clientId: 'api' + clientId: 'api', }); if (!clients.length) { throw new Error('The "api" client has not been created yet.'); diff --git a/services/api/tsconfig.json b/services/api/tsconfig.json index 936278989c..4ca8fb2dc3 100644 --- a/services/api/tsconfig.json +++ b/services/api/tsconfig.json @@ -1,11 +1,16 @@ { - "extends": "../../tsconfig.json", "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "jsx": "react", + "lib": ["es6", "es2017"], + "module": "CommonJS", + "moduleResolution": "node", "outDir": "./dist", - "moduleResolution": "node" + "resolveJsonModule": true, + "skipLibCheck": true, + "target": "ES6" }, - "include": ["./src"], - "references": [ - { "path": "../../node-packages/commons" } - ] -} \ No newline at end of file + "include": ["./src"] +} diff --git a/services/auth-server/.dockerignore b/services/auth-server/.dockerignore index 026d7fd9e7..cc51139370 100644 --- a/services/auth-server/.dockerignore +++ b/services/auth-server/.dockerignore @@ -1,4 +1,3 @@ .dockerignore -.yarn* logs node_modules/* diff --git a/services/auth-server/.eslintrc.js b/services/auth-server/.eslintrc.js index 96000b1b5e..5b7612864f 100644 --- a/services/auth-server/.eslintrc.js +++ b/services/auth-server/.eslintrc.js @@ -1 +1,46 @@ -module.exports = require('eslint-config-lagoon-node'); +module.exports = { + parser: '@typescript-eslint/parser', + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + 'eslint-import-resolver-typescript': true, + }, + }, + extends: ['airbnb-base', 'plugin:@typescript-eslint/recommended'], + plugins: ['@typescript-eslint'], + env: { es6: true, jest: true, node: true }, + rules: { + 'import/extensions': ['error', 'ignorePackages', { ts: 'never', tsx: 'never', js: 'never' }], + 'arrow-parens': 'off', + camelcase: 'off', + 'consistent-return': 'off', + 'function-paren-newline': 'off', + 'global-require': 'off', + 'import/first': 'off', + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: ['**/*.test.js', '**/scripts/*.js'], + }, + ], + 'import/prefer-default-export': 'off', + 'max-len': 'off', + 'no-await-in-loop': 'off', + 'no-confusing-arrow': 'off', + 'no-console': 'off', + 'no-mixed-operators': 'off', + 'no-multi-assign': 'off', + 'no-param-reassign': 'off', + 'no-plusplus': 'off', + 'no-restricted-globals': 'off', + 'no-restricted-syntax': 'off', + 'no-throw-literal': 'off', + 'no-underscore-dangle': 'off', + 'no-use-before-define': 'off', + 'prefer-destructuring': 'off', + 'prefer-promise-reject-errors': 'off', + radix: 'off', + }, +}; diff --git a/services/auth-server/.gitignore b/services/auth-server/.gitignore index 7ec2987077..f06235c460 100644 --- a/services/auth-server/.gitignore +++ b/services/auth-server/.gitignore @@ -1,3 +1,2 @@ node_modules dist -.yarn-error.log diff --git a/services/auth-server/Dockerfile b/services/auth-server/Dockerfile index 6335039db9..a4247cf047 100644 --- a/services/auth-server/Dockerfile +++ b/services/auth-server/Dockerfile @@ -1,35 +1,26 @@ ARG LAGOON_GIT_BRANCH -ARG IMAGE_REPO ARG UPSTREAM_REPO ARG UPSTREAM_TAG -# STAGE 1: Loading Image lagoon-node-packages-builder which contains node packages shared by all Node Services -FROM ${IMAGE_REPO:-lagoon}/yarn-workspace-builder AS yarn-workspace-builder - -# STAGE 2: specific service Image FROM ${UPSTREAM_REPO:-uselagoon}/node-22:${UPSTREAM_TAG:-latest} ARG LAGOON_VERSION ENV LAGOON_VERSION=$LAGOON_VERSION -# Copying generated node_modules from the first stage -COPY --from=yarn-workspace-builder /app /app +WORKDIR /app/ -# Setting the workdir to the service, all following commands will run from here -WORKDIR /app/services/auth-server/ +RUN corepack enable && corepack prepare pnpm@latest --activate -# Copying the .env.defaults into the Workdir, as the dotenv system searches within the workdir for it -COPY --from=yarn-workspace-builder /app/.env.defaults . +# Install dependencies +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile # Copying files from our service COPY . . -# Verify that all dependencies have been installed via the yarn-workspace-builder -RUN yarn check --verify-tree - # Making sure we run in production ENV NODE_ENV=production \ KEYCLOAK_AUTH_SERVER_CLIENT_SECRET=f605b150-7636-4447-abd3-70988786b330 -RUN yarn build +RUN pnpm build -CMD ["yarn", "start"] +CMD ["pnpm", "start"] diff --git a/services/auth-server/package.json b/services/auth-server/package.json index 5502c9e97e..a4c97c914b 100644 --- a/services/auth-server/package.json +++ b/services/auth-server/package.json @@ -15,29 +15,34 @@ "start": "node dist/index", "dev": "NODE_ENV=development nodemon", "format": "prettier-eslint --write '**/*.js'", - "lint": "../../node-packages/eslint-config-lagoon-node/node_modules/.bin/eslint ." + "lint": "eslint ." }, "nodemonConfig": { "watch": [ "src" ], "ext": "js,ts,json", - "exec": "yarn build && yarn start --inspect=0.0.0.0:9229" + "exec": "pnpm build && pnpm start --inspect=0.0.0.0:9229" }, "dependencies": { - "axios": "^1.12.0", - "body-parser": "^1.18.2", - "express": "^4.16.2", + "axios": "^1.13.5", + "body-parser": "^1.20.4", + "express": "^4.22.1", "jsonwebtoken": "^8.5.1", - "morgan": "^1.9.0", + "morgan": "^1.10.1", "ramda": "0.25.0", - "winston": "^3" + "winston": "^3.19.0" }, "devDependencies": { - "@types/jsonwebtoken": "^9.0.8", - "nodemon": "^3.0.3", - "prettier": "^3.2.5", + "@types/jsonwebtoken": "^9.0.10", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", + "eslint": "^8.57.1", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.32.0", + "nodemon": "^3.1.14", + "prettier": "^3.8.1", "prettier-eslint-cli": "^8.0.1", - "typescript": "^5.3.3" + "typescript": "^5.9.3" } } diff --git a/services/auth-server/pnpm-lock.yaml b/services/auth-server/pnpm-lock.yaml new file mode 100644 index 0000000000..067c4893b7 --- /dev/null +++ b/services/auth-server/pnpm-lock.yaml @@ -0,0 +1,3950 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + axios: + specifier: ^1.13.5 + version: 1.13.5 + body-parser: + specifier: ^1.20.4 + version: 1.20.4 + express: + specifier: ^4.22.1 + version: 4.22.1 + jsonwebtoken: + specifier: ^8.5.1 + version: 8.5.1 + morgan: + specifier: ^1.10.1 + version: 1.10.1 + ramda: + specifier: 0.25.0 + version: 0.25.0 + winston: + specifier: ^3.19.0 + version: 3.19.0 + devDependencies: + '@types/jsonwebtoken': + specifier: ^9.0.10 + version: 9.0.10 + '@typescript-eslint/eslint-plugin': + specifier: ^8.56.1 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.56.1 + version: 8.56.1(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.57.1 + version: 8.57.1 + eslint-config-airbnb-base: + specifier: ^15.0.0 + version: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: + specifier: ^2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) + nodemon: + specifier: ^3.1.14 + version: 3.1.14 + prettier: + specifier: ^3.8.1 + version: 3.8.1 + prettier-eslint-cli: + specifier: ^8.0.1 + version: 8.0.1(prettier-eslint@16.4.2(typescript@5.9.3))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@dabh/diagnostics@2.0.8': + resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@messageformat/core@3.4.0': + resolution: {integrity: sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw==} + + '@messageformat/date-skeleton@1.1.0': + resolution: {integrity: sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A==} + + '@messageformat/number-skeleton@1.2.0': + resolution: {integrity: sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==} + + '@messageformat/parser@5.1.1': + resolution: {integrity: sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==} + + '@messageformat/runtime@3.0.2': + resolution: {integrity: sha512-dkIPDCjXcfhSHgNE1/qV6TeczQZR59Yx0xXeafVKgK3QVWoxc38ljwpksUpnzCGvN151KUbCJTDZVmahtf1YZw==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + + '@so-ric/colorspace@1.1.6': + resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/jsonwebtoken@9.0.10': + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@25.3.0': + resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@typescript-eslint/eslint-plugin@8.56.1': + resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.56.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@6.21.0': + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@8.56.1': + resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.56.1': + resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@6.21.0': + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/scope-manager@8.56.1': + resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.56.1': + resolution: {integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@6.21.0': + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/types@8.56.1': + resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@6.21.0': + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.56.1': + resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@6.21.0': + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/visitor-keys@8.56.1': + resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + boolify@1.0.1: + resolution: {integrity: sha512-ma2q0Tc760dW54CdOyJjhrg/a54317o1zYADQJFgperNGKIKgAUGIcKnuMiff8z57+yGlrGNEt4lPgZfCgTJgA==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + brace-expansion@5.0.3: + resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-keys@9.1.3: + resolution: {integrity: sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==} + engines: {node: '>=16'} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + chalk@1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-convert@3.1.3: + resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==} + engines: {node: '>=14.6'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-name@2.1.0: + resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} + engines: {node: '>=12.20'} + + color-string@2.1.4: + resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} + engines: {node: '>=18'} + + color@5.0.3: + resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} + engines: {node: '>=18'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + core-js@3.48.0: + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-airbnb-base@15.0.0: + resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.2 + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.5.0: + resolution: {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 + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + 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 + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-ansi@2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsonwebtoken@8.5.1: + resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} + engines: {node: '>=4', npm: '>=1.4.28'} + + jwa@1.4.2: + resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} + + jws@3.2.3: + resolution: {integrity: sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + + loglevel-colored-level-prefix@1.0.0: + resolution: {integrity: sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==} + + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + make-plural@7.5.0: + resolution: {integrity: sha512-0booA+aVYyVFoR67JBHdfVk0U08HmrBH2FrtmBqBa+NldlqXv/G2Z9VQuQq6Wgp2jDWdybEWGfBkk1cq5264WA==} + + map-obj@5.0.0: + resolution: {integrity: sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + minimatch@10.2.2: + resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.3: + resolution: {integrity: sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.6: + resolution: {integrity: sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + + morgan@1.10.1: + resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==} + engines: {node: '>= 0.8.0'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + nodemon@3.1.14: + resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==} + engines: {node: '>=10'} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-eslint-cli@8.0.1: + resolution: {integrity: sha512-jru4JUDHzWEtM/SOxqagU7hQTVP8BVrxO2J0qNauWZuPRld6Ea2eyNaEzIGx6I+yjmOLCsjNM+vU1AJgaW1ZSQ==} + engines: {node: '>=16.10.0'} + hasBin: true + peerDependencies: + prettier-eslint: '*' + peerDependenciesMeta: + prettier-eslint: + optional: true + + prettier-eslint@16.4.2: + resolution: {integrity: sha512-vtJAQEkaN8fW5QKl08t7A5KCjlZuDUNeIlr9hgolMS5s3+uzbfRHDwaRnzrdqnY2YpHDmeDS/8zY0MKQHXJtaA==} + engines: {node: '>=16.10.0'} + peerDependencies: + prettier-plugin-svelte: ^3.0.0 + svelte-eslint-parser: '*' + peerDependenciesMeta: + prettier-plugin-svelte: + optional: true + svelte-eslint-parser: + optional: true + + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.2: + resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@6.1.2: + resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==} + engines: {node: '>=12'} + + ramda@0.25.0: + resolution: {integrity: sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-relative@0.8.7: + resolution: {integrity: sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-identifier@0.4.2: + resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.19.0: + resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@colors/colors@1.6.0': {} + + '@dabh/diagnostics@2.0.8': + dependencies: + '@so-ric/colorspace': 1.1.6 + enabled: 2.0.0 + kuler: 2.0.0 + + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.14.0 + debug: 4.4.3(supports-color@5.5.0) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.3 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3(supports-color@5.5.0) + minimatch: 3.1.3 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.10 + + '@messageformat/core@3.4.0': + dependencies: + '@messageformat/date-skeleton': 1.1.0 + '@messageformat/number-skeleton': 1.2.0 + '@messageformat/parser': 5.1.1 + '@messageformat/runtime': 3.0.2 + make-plural: 7.5.0 + safe-identifier: 0.4.2 + + '@messageformat/date-skeleton@1.1.0': {} + + '@messageformat/number-skeleton@1.2.0': {} + + '@messageformat/parser@5.1.1': + dependencies: + moo: 0.5.2 + + '@messageformat/runtime@3.0.2': + dependencies: + make-plural: 7.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@sinclair/typebox@0.27.10': {} + + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.3 + text-hex: 1.0.0 + + '@types/json5@0.0.29': {} + + '@types/jsonwebtoken@9.0.10': + dependencies: + '@types/ms': 2.1.0 + '@types/node': 25.3.0 + + '@types/ms@2.1.0': {} + + '@types/node@25.3.0': + dependencies: + undici-types: 7.18.2 + + '@types/triple-beam@1.3.5': {} + + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/type-utils': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.56.1 + eslint: 8.57.1 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.3(supports-color@5.5.0) + eslint: 8.57.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3(supports-color@5.5.0) + eslint: 8.57.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) + '@typescript-eslint/types': 8.56.1 + debug: 4.4.3(supports-color@5.5.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + + '@typescript-eslint/scope-manager@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.56.1(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + debug: 4.4.3(supports-color@5.5.0) + eslint: 8.57.1 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@6.21.0': {} + + '@typescript-eslint/types@8.56.1': {} + + '@typescript-eslint/typescript-estree@6.21.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.3(supports-color@5.5.0) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.7.4 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3(supports-color@5.5.0) + minimatch: 10.2.2 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + eslint: 8.57.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + eslint-visitor-keys: 5.0.1 + + '@ungap/structured-clone@1.3.0': {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@2.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@2.2.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-flatten@1.1.1: {} + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array-union@2.1.0: {} + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + arrify@2.0.1: {} + + async-function@1.0.0: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios@1.13.5: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + + binary-extensions@2.3.0: {} + + body-parser@1.20.4: + 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.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.2 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + boolify@1.0.1: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.3: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer-equal-constant-time@1.0.1: {} + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase-keys@9.1.3: + dependencies: + camelcase: 8.0.0 + map-obj: 5.0.0 + quick-lru: 6.1.2 + type-fest: 4.41.0 + + camelcase@8.0.0: {} + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-convert@3.1.3: + dependencies: + color-name: 2.1.0 + + color-name@1.1.4: {} + + color-name@2.1.0: {} + + color-string@2.1.4: + dependencies: + color-name: 2.1.0 + + color@5.0.3: + dependencies: + color-convert: 3.1.3 + color-string: 2.1.4 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + common-tags@1.8.2: {} + + concat-map@0.0.1: {} + + confusing-browser-globals@1.0.11: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.7: {} + + cookie@0.7.2: {} + + core-js@3.48.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enabled@2.0.0: {} + + encodeurl@2.0.0: {} + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1): + dependencies: + confusing-browser-globals: 1.0.11 + eslint: 8.57.1 + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) + object.assign: 4.1.7 + object.entries: 1.1.9 + semver: 6.3.1 + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.3 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@5.5.0) + 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.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.3 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + 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.14.2 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fecha@4.2.3: {} + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.2: + dependencies: + 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 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.3: {} + + fn.name@1.1.0: {} + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.5: + 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 + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + 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-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stdin@8.0.0: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.6 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.3 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graphemer@1.4.0: {} + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + ignore-by-default@1.0.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + ipaddr.js@1.9.1: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + jsonwebtoken@8.5.1: + dependencies: + jws: 3.2.3 + 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.1.1 + ms: 2.1.3 + semver: 5.7.2 + + jwa@1.4.2: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.3: + dependencies: + jwa: 1.4.2 + safe-buffer: 5.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kuler@2.0.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + 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.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash@4.17.23: {} + + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + + loglevel-colored-level-prefix@1.0.0: + dependencies: + chalk: 1.1.3 + loglevel: 1.9.2 + + loglevel@1.9.2: {} + + lru-cache@10.4.3: {} + + make-plural@7.5.0: {} + + map-obj@5.0.0: {} + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + merge-descriptors@1.0.3: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + minimatch@10.2.2: + dependencies: + brace-expansion: 5.0.3 + + minimatch@3.1.3: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.6: + dependencies: + brace-expansion: 5.0.3 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + moo@0.5.2: {} + + morgan@1.10.1: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.1.0 + transitivePeerDependencies: + - supports-color + + ms@2.0.0: {} + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + nodemon@3.1.14: + dependencies: + chokidar: 3.6.0 + debug: 4.4.3(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 10.2.2 + pstree.remy: 1.1.8 + semver: 7.7.4 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + normalize-path@3.0.0: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-to-regexp@0.1.12: {} + + path-type@4.0.0: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + prelude-ls@1.2.1: {} + + prettier-eslint-cli@8.0.1(prettier-eslint@16.4.2(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@messageformat/core': 3.4.0 + '@prettier/eslint': prettier-eslint@16.4.2(typescript@5.9.3) + arrify: 2.0.1 + boolify: 1.0.1 + camelcase-keys: 9.1.3 + chalk: 4.1.2 + common-tags: 1.8.2 + core-js: 3.48.0 + eslint: 8.57.1 + find-up: 5.0.0 + get-stdin: 8.0.0 + glob: 10.5.0 + ignore: 5.3.2 + indent-string: 4.0.0 + lodash.memoize: 4.1.2 + loglevel-colored-level-prefix: 1.0.0 + rxjs: 7.8.2 + yargs: 17.7.2 + optionalDependencies: + prettier-eslint: 16.4.2(typescript@5.9.3) + transitivePeerDependencies: + - prettier-plugin-svelte + - supports-color + - svelte-eslint-parser + - typescript + + prettier-eslint@16.4.2(typescript@5.9.3): + dependencies: + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + common-tags: 1.8.2 + dlv: 1.1.3 + eslint: 8.57.1 + indent-string: 4.0.0 + lodash.merge: 4.6.2 + loglevel-colored-level-prefix: 1.0.0 + prettier: 3.8.1 + pretty-format: 29.7.0 + require-relative: 0.8.7 + tslib: 2.8.1 + vue-eslint-parser: 9.4.3(eslint@8.57.1) + transitivePeerDependencies: + - supports-color + - typescript + + prettier@3.8.1: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@1.1.0: {} + + pstree.remy@1.1.8: {} + + punycode@2.3.1: {} + + qs@6.14.2: + dependencies: + side-channel: 1.1.0 + + queue-microtask@1.2.3: {} + + quick-lru@6.1.2: {} + + ramda@0.25.0: {} + + range-parser@1.2.1: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + react-is@18.3.1: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + require-directory@2.1.1: {} + + require-relative@0.8.7: {} + + resolve-from@4.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-identifier@0.4.2: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.7.4: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + 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: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@4.1.0: {} + + simple-update-notifier@2.0.0: + dependencies: + semver: 7.7.4 + + slash@3.0.0: {} + + stack-trace@0.0.10: {} + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@2.0.0: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + text-hex@1.0.0: {} + + text-table@0.2.0: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + touch@3.1.1: {} + + triple-beam@1.4.1: {} + + ts-api-utils@1.4.3(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-fest@4.41.0: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undefsafe@2.0.5: {} + + undici-types@7.18.2: {} + + unpipe@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + vary@1.1.2: {} + + vue-eslint-parser@9.4.3(eslint@8.57.1): + dependencies: + debug: 4.4.3(supports-color@5.5.0) + eslint: 8.57.1 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + lodash: 4.17.23 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + 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 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.19.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.8 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} diff --git a/services/auth-server/src/index.ts b/services/auth-server/src/index.ts index 0763f15bea..3b2927fac7 100644 --- a/services/auth-server/src/index.ts +++ b/services/auth-server/src/index.ts @@ -21,8 +21,8 @@ const app = express(); app.use( morgan('combined', { stream: { - write: message => logger.info(message.trim()) - } + write: message => logger.info(message.trim()), + }, }), ); @@ -72,7 +72,7 @@ app.use((err: LagoonErrorWithStatus, req: Request, res: Response, next: NextFunc logger.error(err.toString()); if (res.headersSent) { - return next(err) + return next(err); } res.status(err.status || 500); diff --git a/services/auth-server/src/logger.ts b/services/auth-server/src/logger.ts index 8f6f9894c4..c928e26032 100644 --- a/services/auth-server/src/logger.ts +++ b/services/auth-server/src/logger.ts @@ -1,6 +1,22 @@ -const { addColors, createLogger, format, transports } = require('winston'); -import { getConfigFromEnv } from '@lagoon/commons/dist/util/config'; -import { levels, colors } from '@lagoon/commons/dist/logs/'; +import { + addColors, createLogger, format, transports, +} from 'winston'; + +const getConfigFromEnv = (name: string, fallback: string = ''): string => process.env[name] || fallback; + +const levels = { + fatal: 0, error: 1, warn: 2, info: 3, verbose: 4, debug: 5, trace: 6, +}; + +const colors = { + fatal: 'red', + error: 'red', + warn: 'yellow', + info: 'magenta', + verbose: 'white', + debug: 'gray', + trace: 'cyan', +}; addColors(colors); @@ -12,15 +28,13 @@ export const logger = createLogger({ format.splat(), format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), format.printf( - ({ timestamp, level, message }) => `[${timestamp}] [${level}]: ${message}` - ) + ({ timestamp, level, message }) => `[${timestamp}] [${level}]: ${message}`, + ), ), transports: [ new transports.Console({ level: getConfigFromEnv('CONSOLE_LOGGING_LEVEL', 'info'), handleExceptions: true, - json: false, - colorize: true - }) - ] + }), + ], }); diff --git a/services/auth-server/src/routes.ts b/services/auth-server/src/routes.ts index 054da01a9e..7179613106 100644 --- a/services/auth-server/src/routes.ts +++ b/services/auth-server/src/routes.ts @@ -10,7 +10,7 @@ declare type keycloakGrant = { export const generateRoute = (getUserGrant: (userId: string) => Promise) => { const route = async (req: Request, res: Response) => { - const userId: any = R.path(['body', 'userId'], req); + const userId = R.path(['body', 'userId'], req); const verbose = R.pathOr(false, ['body', 'verbose'], req); const returnGrant = R.pathOr(false, ['body', 'grant'], req); diff --git a/services/auth-server/src/util/auth.ts b/services/auth-server/src/util/auth.ts index 5e8dcbd6da..19894a6962 100644 --- a/services/auth-server/src/util/auth.ts +++ b/services/auth-server/src/util/auth.ts @@ -1,33 +1,22 @@ -import R from 'ramda'; import JWT from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import { logger } from '../logger'; const { JWTSECRET, JWTAUDIENCE } = process.env; -const parseBearerToken = R.compose( - R.ifElse( - (splits: any) => - R.length(splits) === 2 && - R.compose( - R.toLower, - R.defaultTo(''), - R.head, - // @ts-ignore - )(splits) === 'bearer', - R.nth(1), - R.always(null), - ), - R.split(' '), - R.defaultTo(''), -); +const parseBearerToken = (header: string | undefined): string | null => { + const parts = (header || '').split(' '); + if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') { + return parts[1]; + } + return null; +}; export const validateToken = async ( req: Request, res: Response, next: NextFunction, ) => { - // @ts-ignore const token = parseBearerToken(req.get('Authorization')); if (token == null) { @@ -58,7 +47,6 @@ export const validateToken = async ( } next(); - return; } catch (e) { return res.status(403).send({ errors: [{ message: `Forbidden - Invalid Auth Token: ${e.message}` }], diff --git a/services/auth-server/test/test-lint b/services/auth-server/test/test-lint index f2327dbe53..4dec7ee1f7 100755 --- a/services/auth-server/test/test-lint +++ b/services/auth-server/test/test-lint @@ -7,4 +7,4 @@ # https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md echo "Starting lint test" -docker run --rm ${IMAGE_NAME} yarn run lint \ No newline at end of file +docker run --rm ${IMAGE_NAME} pnpm run lint diff --git a/services/auth-server/tsconfig.json b/services/auth-server/tsconfig.json index 6a62dbc484..38bdb2b49b 100644 --- a/services/auth-server/tsconfig.json +++ b/services/auth-server/tsconfig.json @@ -1,6 +1,13 @@ { - "extends": "../../tsconfig.json", "compilerOptions": { + "allowJs": true, + "module": "CommonJS", + "skipLibCheck": true, + "lib": ["es6", "es2017"], + "target": "ES6", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, "outDir": "./dist" }, "include": ["./src"] diff --git a/services/backup-handler/go.mod b/services/backup-handler/go.mod index 7977a26003..839bf2a99a 100644 --- a/services/backup-handler/go.mod +++ b/services/backup-handler/go.mod @@ -2,7 +2,7 @@ module github.com/uselagoon/lagoon/services/backup-handler go 1.25 -require github.com/uselagoon/machinery v0.0.36-0.20260223222038-8118f3c2cb49 +require github.com/uselagoon/machinery v0.0.36-0.20260220001712-f1a24d1f8deb require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect diff --git a/services/backup-handler/go.sum b/services/backup-handler/go.sum index 667440ae84..2991e121f0 100644 --- a/services/backup-handler/go.sum +++ b/services/backup-handler/go.sum @@ -14,5 +14,3 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/uselagoon/machinery v0.0.36-0.20260220001712-f1a24d1f8deb h1:/HQOkNcgkYsFI5C9yDHrICjjdUL73dVmYwUb3u+NZqQ= github.com/uselagoon/machinery v0.0.36-0.20260220001712-f1a24d1f8deb/go.mod h1:UVqIxwF/Q9xO3LQMkQhWeuegpuKcsrxmBa4LE52SiWQ= -github.com/uselagoon/machinery v0.0.36-0.20260223222038-8118f3c2cb49 h1:efS638Mq1WMkJZKj+8rUTZq9CY1WP9Qs0dxJqk0OJRM= -github.com/uselagoon/machinery v0.0.36-0.20260223222038-8118f3c2cb49/go.mod h1:UVqIxwF/Q9xO3LQMkQhWeuegpuKcsrxmBa4LE52SiWQ= diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 23ebbd9c3d..0000000000 --- a/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "module": "CommonJS", - "skipLibCheck": true, - "lib": ["es6", "es2017"], - "target": "ES6", - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "resolveJsonModule": true, - "jsx": "react" - } -} diff --git a/yarn-workspace-builder/Dockerfile b/yarn-workspace-builder/Dockerfile deleted file mode 100644 index 420c06c252..0000000000 --- a/yarn-workspace-builder/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -ARG UPSTREAM_REPO -ARG UPSTREAM_TAG -FROM ${UPSTREAM_REPO:-uselagoon}/node-22-builder:${UPSTREAM_TAG:-latest} - -COPY package.json yarn.lock .env.defaults tsconfig.json /app/ -COPY node-packages /app/node-packages - -# We need to copy all services, so we have all -# package.json files for workspaces, otherwise -# subdependencies won't be installed -COPY services/api/package.json /app/services/api/ -COPY services/auth-server/package.json /app/services/auth-server/ - -RUN yarn install --frozen-lockfile --network-timeout 600000 - -RUN cd /app/node-packages/commons && yarn build diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 49cce4806f..0000000000 --- a/yarn.lock +++ /dev/null @@ -1,10519 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0": - version "3.13.9" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.13.9.tgz#311e0df1734aaa5598e01a292bb7982df49cc062" - integrity sha512-RStSzQfL1XwL6/NWd7W8avhGQYTgPCtJ+qHkkTTSj9Upp3VVm6Oppv81YWdXG1FgEpDPW4hvCrTUELdcC4inCQ== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - "@wry/caches" "^1.0.0" - "@wry/equality" "^0.5.6" - "@wry/trie" "^0.5.0" - graphql-tag "^2.12.6" - hoist-non-react-statics "^3.3.2" - optimism "^0.18.0" - prop-types "^15.7.2" - rehackt "^0.1.0" - symbol-observable "^4.0.0" - ts-invariant "^0.10.3" - tslib "^2.3.0" - zen-observable-ts "^1.2.5" - -"@apollo/protobufjs@1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.6.tgz#d601e65211e06ae1432bf5993a1a0105f2862f27" - integrity sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.0" - "@types/node" "^10.1.0" - long "^4.0.0" - -"@apollo/protobufjs@1.2.7": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.7.tgz#3a8675512817e4a046a897e5f4f16415f16a7d8a" - integrity sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.0" - long "^4.0.0" - -"@apollo/usage-reporting-protobuf@^4.0.0": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz#407c3d18c7fbed7a264f3b9a3812620b93499de1" - integrity sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA== - dependencies: - "@apollo/protobufjs" "1.2.7" - -"@apollo/utils.dropunuseddefinitions@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz#02b04006442eaf037f4c4624146b12775d70d929" - integrity sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg== - -"@apollo/utils.keyvaluecache@^1.0.1": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz#2bfe358c4d82f3a0950518451996758c52613f57" - integrity sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg== - dependencies: - "@apollo/utils.logger" "^1.0.0" - lru-cache "7.10.1 - 7.13.1" - -"@apollo/utils.logger@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@apollo/utils.logger/-/utils.logger-1.0.1.tgz#aea0d1bb7ceb237f506c6bbf38f10a555b99a695" - integrity sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA== - -"@apollo/utils.printwithreducedwhitespace@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz#c466299a4766eef8577a2a64c8f27712e8bd7e30" - integrity sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q== - -"@apollo/utils.removealiases@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz#75f6d83098af1fcae2d3beb4f515ad4a8452a8c1" - integrity sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A== - -"@apollo/utils.sortast@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz#93218c7008daf3e2a0725196085a33f5aab5ad07" - integrity sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA== - dependencies: - lodash.sortby "^4.7.0" - -"@apollo/utils.stripsensitiveliterals@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz#4920651f36beee8e260e12031a0c5863ad0c7b28" - integrity sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w== - -"@apollo/utils.usagereporting@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.1.tgz#3c70b49e554771659576fe35381c7a4b321d27fd" - integrity sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ== - dependencies: - "@apollo/usage-reporting-protobuf" "^4.0.0" - "@apollo/utils.dropunuseddefinitions" "^1.1.0" - "@apollo/utils.printwithreducedwhitespace" "^1.1.0" - "@apollo/utils.removealiases" "1.0.0" - "@apollo/utils.sortast" "^1.1.0" - "@apollo/utils.stripsensitiveliterals" "^1.2.0" - -"@apollographql/apollo-tools@^0.5.3": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz#cb3998c6cf12e494b90c733f44dd9935e2d8196c" - integrity sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw== - -"@apollographql/graphql-playground-html@1.6.29": - version "1.6.29" - resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz#a7a646614a255f62e10dcf64a7f68ead41dec453" - integrity sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA== - dependencies: - xss "^1.0.8" - -"@aws-crypto/crc32@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" - integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== - dependencies: - "@aws-crypto/util" "^5.2.0" - "@aws-sdk/types" "^3.222.0" - tslib "^2.6.2" - -"@aws-crypto/crc32c@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz#4e34aab7f419307821509a98b9b08e84e0c1917e" - integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== - dependencies: - "@aws-crypto/util" "^5.2.0" - "@aws-sdk/types" "^3.222.0" - tslib "^2.6.2" - -"@aws-crypto/sha1-browser@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz#b0ee2d2821d3861f017e965ef3b4cb38e3b6a0f4" - integrity sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== - dependencies: - "@aws-crypto/supports-web-crypto" "^5.2.0" - "@aws-crypto/util" "^5.2.0" - "@aws-sdk/types" "^3.222.0" - "@aws-sdk/util-locate-window" "^3.0.0" - "@smithy/util-utf8" "^2.0.0" - tslib "^2.6.2" - -"@aws-crypto/sha256-browser@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" - integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== - dependencies: - "@aws-crypto/sha256-js" "^5.2.0" - "@aws-crypto/supports-web-crypto" "^5.2.0" - "@aws-crypto/util" "^5.2.0" - "@aws-sdk/types" "^3.222.0" - "@aws-sdk/util-locate-window" "^3.0.0" - "@smithy/util-utf8" "^2.0.0" - tslib "^2.6.2" - -"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" - integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== - dependencies: - "@aws-crypto/util" "^5.2.0" - "@aws-sdk/types" "^3.222.0" - tslib "^2.6.2" - -"@aws-crypto/supports-web-crypto@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" - integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== - dependencies: - tslib "^2.6.2" - -"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" - integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== - dependencies: - "@aws-sdk/types" "^3.222.0" - "@smithy/util-utf8" "^2.0.0" - tslib "^2.6.2" - -"@aws-sdk/client-s3@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.929.0.tgz#9d34ffa708f5e3a0bc39859df51cb55b051054ad" - integrity sha512-M6G+1CBTowN+m0Jrww5/AXMqlk4nIJqwaa/vOw+EbvLD7ROpBs6bStSai9esP9PkIVW6KMu4zCIgHzKhGa3R2A== - dependencies: - "@aws-crypto/sha1-browser" "5.2.0" - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.928.0" - "@aws-sdk/credential-provider-node" "3.929.0" - "@aws-sdk/middleware-bucket-endpoint" "3.922.0" - "@aws-sdk/middleware-expect-continue" "3.922.0" - "@aws-sdk/middleware-flexible-checksums" "3.928.0" - "@aws-sdk/middleware-host-header" "3.922.0" - "@aws-sdk/middleware-location-constraint" "3.922.0" - "@aws-sdk/middleware-logger" "3.922.0" - "@aws-sdk/middleware-recursion-detection" "3.922.0" - "@aws-sdk/middleware-sdk-s3" "3.928.0" - "@aws-sdk/middleware-ssec" "3.922.0" - "@aws-sdk/middleware-user-agent" "3.928.0" - "@aws-sdk/region-config-resolver" "3.925.0" - "@aws-sdk/signature-v4-multi-region" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@aws-sdk/util-endpoints" "3.922.0" - "@aws-sdk/util-user-agent-browser" "3.922.0" - "@aws-sdk/util-user-agent-node" "3.928.0" - "@aws-sdk/xml-builder" "3.921.0" - "@smithy/config-resolver" "^4.4.2" - "@smithy/core" "^3.17.2" - "@smithy/eventstream-serde-browser" "^4.2.4" - "@smithy/eventstream-serde-config-resolver" "^4.3.4" - "@smithy/eventstream-serde-node" "^4.2.4" - "@smithy/fetch-http-handler" "^5.3.5" - "@smithy/hash-blob-browser" "^4.2.5" - "@smithy/hash-node" "^4.2.4" - "@smithy/hash-stream-node" "^4.2.4" - "@smithy/invalid-dependency" "^4.2.4" - "@smithy/md5-js" "^4.2.4" - "@smithy/middleware-content-length" "^4.2.4" - "@smithy/middleware-endpoint" "^4.3.6" - "@smithy/middleware-retry" "^4.4.6" - "@smithy/middleware-serde" "^4.2.4" - "@smithy/middleware-stack" "^4.2.4" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/node-http-handler" "^4.4.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/smithy-client" "^4.9.2" - "@smithy/types" "^4.8.1" - "@smithy/url-parser" "^4.2.4" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-body-length-node" "^4.2.1" - "@smithy/util-defaults-mode-browser" "^4.3.5" - "@smithy/util-defaults-mode-node" "^4.2.8" - "@smithy/util-endpoints" "^3.2.4" - "@smithy/util-middleware" "^4.2.4" - "@smithy/util-retry" "^4.2.4" - "@smithy/util-stream" "^4.5.5" - "@smithy/util-utf8" "^4.2.0" - "@smithy/util-waiter" "^4.2.4" - "@smithy/uuid" "^1.1.0" - tslib "^2.6.2" - -"@aws-sdk/client-s3@^3.828.0": - version "3.918.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.918.0.tgz#504a270277f556b9ca5c56ff9fc5596972da0940" - integrity sha512-25DhKO0QB4QbhbX1t+txCoRNRvchcq9s3lrDrVJLDwpS7e3cTwSOsicyvMpme6Wk/NSln/lWkYazx8MgUbO6RA== - dependencies: - "@aws-crypto/sha1-browser" "5.2.0" - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.916.0" - "@aws-sdk/credential-provider-node" "3.918.0" - "@aws-sdk/middleware-bucket-endpoint" "3.914.0" - "@aws-sdk/middleware-expect-continue" "3.917.0" - "@aws-sdk/middleware-flexible-checksums" "3.916.0" - "@aws-sdk/middleware-host-header" "3.914.0" - "@aws-sdk/middleware-location-constraint" "3.914.0" - "@aws-sdk/middleware-logger" "3.914.0" - "@aws-sdk/middleware-recursion-detection" "3.914.0" - "@aws-sdk/middleware-sdk-s3" "3.916.0" - "@aws-sdk/middleware-ssec" "3.914.0" - "@aws-sdk/middleware-user-agent" "3.916.0" - "@aws-sdk/region-config-resolver" "3.914.0" - "@aws-sdk/signature-v4-multi-region" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@aws-sdk/util-endpoints" "3.916.0" - "@aws-sdk/util-user-agent-browser" "3.914.0" - "@aws-sdk/util-user-agent-node" "3.916.0" - "@aws-sdk/xml-builder" "3.914.0" - "@smithy/config-resolver" "^4.4.0" - "@smithy/core" "^3.17.1" - "@smithy/eventstream-serde-browser" "^4.2.3" - "@smithy/eventstream-serde-config-resolver" "^4.3.3" - "@smithy/eventstream-serde-node" "^4.2.3" - "@smithy/fetch-http-handler" "^5.3.4" - "@smithy/hash-blob-browser" "^4.2.4" - "@smithy/hash-node" "^4.2.3" - "@smithy/hash-stream-node" "^4.2.3" - "@smithy/invalid-dependency" "^4.2.3" - "@smithy/md5-js" "^4.2.3" - "@smithy/middleware-content-length" "^4.2.3" - "@smithy/middleware-endpoint" "^4.3.5" - "@smithy/middleware-retry" "^4.4.5" - "@smithy/middleware-serde" "^4.2.3" - "@smithy/middleware-stack" "^4.2.3" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/node-http-handler" "^4.4.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - "@smithy/url-parser" "^4.2.3" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-body-length-node" "^4.2.1" - "@smithy/util-defaults-mode-browser" "^4.3.4" - "@smithy/util-defaults-mode-node" "^4.2.6" - "@smithy/util-endpoints" "^3.2.3" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-retry" "^4.2.3" - "@smithy/util-stream" "^4.5.4" - "@smithy/util-utf8" "^4.2.0" - "@smithy/util-waiter" "^4.2.3" - "@smithy/uuid" "^1.1.0" - tslib "^2.6.2" - -"@aws-sdk/client-sso@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.916.0.tgz#627792ab588a004fc0874a060b3466e21328b5b6" - integrity sha512-Eu4PtEUL1MyRvboQnoq5YKg0Z9vAni3ccebykJy615xokVZUdA3di2YxHM/hykDQX7lcUC62q9fVIvh0+UNk/w== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.916.0" - "@aws-sdk/middleware-host-header" "3.914.0" - "@aws-sdk/middleware-logger" "3.914.0" - "@aws-sdk/middleware-recursion-detection" "3.914.0" - "@aws-sdk/middleware-user-agent" "3.916.0" - "@aws-sdk/region-config-resolver" "3.914.0" - "@aws-sdk/types" "3.914.0" - "@aws-sdk/util-endpoints" "3.916.0" - "@aws-sdk/util-user-agent-browser" "3.914.0" - "@aws-sdk/util-user-agent-node" "3.916.0" - "@smithy/config-resolver" "^4.4.0" - "@smithy/core" "^3.17.1" - "@smithy/fetch-http-handler" "^5.3.4" - "@smithy/hash-node" "^4.2.3" - "@smithy/invalid-dependency" "^4.2.3" - "@smithy/middleware-content-length" "^4.2.3" - "@smithy/middleware-endpoint" "^4.3.5" - "@smithy/middleware-retry" "^4.4.5" - "@smithy/middleware-serde" "^4.2.3" - "@smithy/middleware-stack" "^4.2.3" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/node-http-handler" "^4.4.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - "@smithy/url-parser" "^4.2.3" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-body-length-node" "^4.2.1" - "@smithy/util-defaults-mode-browser" "^4.3.4" - "@smithy/util-defaults-mode-node" "^4.2.6" - "@smithy/util-endpoints" "^3.2.3" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-retry" "^4.2.3" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/client-sso@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.929.0.tgz#82f85ac8a15ed296b94f391477a5f417bbc31549" - integrity sha512-CE1T7PvN2MDRCw96BTUz2Zcnb6Lae3Dl4w3TPB5auBv2sAiIPbQegFUwT2C8teMDGCNXyndzoTvAd4wmO9AcpA== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.928.0" - "@aws-sdk/middleware-host-header" "3.922.0" - "@aws-sdk/middleware-logger" "3.922.0" - "@aws-sdk/middleware-recursion-detection" "3.922.0" - "@aws-sdk/middleware-user-agent" "3.928.0" - "@aws-sdk/region-config-resolver" "3.925.0" - "@aws-sdk/types" "3.922.0" - "@aws-sdk/util-endpoints" "3.922.0" - "@aws-sdk/util-user-agent-browser" "3.922.0" - "@aws-sdk/util-user-agent-node" "3.928.0" - "@smithy/config-resolver" "^4.4.2" - "@smithy/core" "^3.17.2" - "@smithy/fetch-http-handler" "^5.3.5" - "@smithy/hash-node" "^4.2.4" - "@smithy/invalid-dependency" "^4.2.4" - "@smithy/middleware-content-length" "^4.2.4" - "@smithy/middleware-endpoint" "^4.3.6" - "@smithy/middleware-retry" "^4.4.6" - "@smithy/middleware-serde" "^4.2.4" - "@smithy/middleware-stack" "^4.2.4" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/node-http-handler" "^4.4.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/smithy-client" "^4.9.2" - "@smithy/types" "^4.8.1" - "@smithy/url-parser" "^4.2.4" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-body-length-node" "^4.2.1" - "@smithy/util-defaults-mode-browser" "^4.3.5" - "@smithy/util-defaults-mode-node" "^4.2.8" - "@smithy/util-endpoints" "^3.2.4" - "@smithy/util-middleware" "^4.2.4" - "@smithy/util-retry" "^4.2.4" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/core@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.916.0.tgz#ea11b485f837f1773e174f8a4ed82ecce9f163f7" - integrity sha512-1JHE5s6MD5PKGovmx/F1e01hUbds/1y3X8rD+Gvi/gWVfdg5noO7ZCerpRsWgfzgvCMZC9VicopBqNHCKLykZA== - dependencies: - "@aws-sdk/types" "3.914.0" - "@aws-sdk/xml-builder" "3.914.0" - "@smithy/core" "^3.17.1" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/property-provider" "^4.2.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/signature-v4" "^5.3.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/core@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.928.0.tgz#abbb1ad9e6f1ab0ea951245aa90a92f59f8722c5" - integrity sha512-e28J2uKjy2uub4u41dNnmzAu0AN3FGB+LRcLN2Qnwl9Oq3kIcByl5sM8ZD+vWpNG+SFUrUasBCq8cMnHxwXZ4w== - dependencies: - "@aws-sdk/types" "3.922.0" - "@aws-sdk/xml-builder" "3.921.0" - "@smithy/core" "^3.17.2" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/property-provider" "^4.2.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/signature-v4" "^5.3.4" - "@smithy/smithy-client" "^4.9.2" - "@smithy/types" "^4.8.1" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-middleware" "^4.2.4" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-env@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.916.0.tgz#c76861ec87f9edf227af62474411bf54ca04805d" - integrity sha512-3gDeqOXcBRXGHScc6xb7358Lyf64NRG2P08g6Bu5mv1Vbg9PKDyCAZvhKLkG7hkdfAM8Yc6UJNhbFxr1ud/tCQ== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/property-provider" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-env@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.928.0.tgz#4f6f59ee3504b208e2b36af66dcd56b1d0e9aa2f" - integrity sha512-tB8F9Ti0/NFyFVQX8UQtgRik88evtHpyT6WfXOB4bAY6lEnEHA0ubJZmk9y+aUeoE+OsGLx70dC3JUsiiCPJkQ== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@smithy/property-provider" "^4.2.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-http@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.916.0.tgz#b46e51c5cc65364c5fde752b4d016b5b747c6d89" - integrity sha512-NmooA5Z4/kPFJdsyoJgDxuqXC1C6oPMmreJjbOPqcwo6E/h2jxaG8utlQFgXe5F9FeJsMx668dtxVxSYnAAqHQ== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/fetch-http-handler" "^5.3.4" - "@smithy/node-http-handler" "^4.4.3" - "@smithy/property-provider" "^4.2.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - "@smithy/util-stream" "^4.5.4" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-http@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.928.0.tgz#6ca904bcda2e89c866a4209e2f5feff238da258e" - integrity sha512-67ynC/8UW9Y8Gn1ZZtC3OgcQDGWrJelHmkbgpmmxYUrzVhp+NINtz3wiTzrrBFhPH/8Uy6BxvhMfXhn0ptcMEQ== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@smithy/fetch-http-handler" "^5.3.5" - "@smithy/node-http-handler" "^4.4.4" - "@smithy/property-provider" "^4.2.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/smithy-client" "^4.9.2" - "@smithy/types" "^4.8.1" - "@smithy/util-stream" "^4.5.5" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-ini@3.918.0": - version "3.918.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.918.0.tgz#a9acc847e709903d39413e8a963e622711a25535" - integrity sha512-oDViX9z4o8jShY0unX9T7MJqyt+/ojhRB2zoLQVr0Mln7GbXwJ0aUtxgb4PFROG27pJpR11oAaZHzI3LI0jm/A== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/credential-provider-env" "3.916.0" - "@aws-sdk/credential-provider-http" "3.916.0" - "@aws-sdk/credential-provider-process" "3.916.0" - "@aws-sdk/credential-provider-sso" "3.916.0" - "@aws-sdk/credential-provider-web-identity" "3.918.0" - "@aws-sdk/nested-clients" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/credential-provider-imds" "^4.2.3" - "@smithy/property-provider" "^4.2.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-ini@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.929.0.tgz#45e08e5163ac6bdbb279892f56ec104508bd71f5" - integrity sha512-XIzWsJUYeS/DjggHFB53sGGjXdlN/BA6x+Y/JvLbpdkGD2yLISU34/cDPbK/O8BAQCRTCQ69VPa/1AdNgZZRQw== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/credential-provider-env" "3.928.0" - "@aws-sdk/credential-provider-http" "3.928.0" - "@aws-sdk/credential-provider-process" "3.928.0" - "@aws-sdk/credential-provider-sso" "3.929.0" - "@aws-sdk/credential-provider-web-identity" "3.929.0" - "@aws-sdk/nested-clients" "3.929.0" - "@aws-sdk/types" "3.922.0" - "@smithy/credential-provider-imds" "^4.2.4" - "@smithy/property-provider" "^4.2.4" - "@smithy/shared-ini-file-loader" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-node@3.918.0": - version "3.918.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.918.0.tgz#8a7364f7779c1508da34bfce4aaadaa890905282" - integrity sha512-gl9ECsPB1i8UBPrAJV0HcTn+sgYuD3jYy8ps6KK4c8LznFizwgpah1jd3eF4qq3kPGzrdAE3MKua9OlCCNWAKQ== - dependencies: - "@aws-sdk/credential-provider-env" "3.916.0" - "@aws-sdk/credential-provider-http" "3.916.0" - "@aws-sdk/credential-provider-ini" "3.918.0" - "@aws-sdk/credential-provider-process" "3.916.0" - "@aws-sdk/credential-provider-sso" "3.916.0" - "@aws-sdk/credential-provider-web-identity" "3.918.0" - "@aws-sdk/types" "3.914.0" - "@smithy/credential-provider-imds" "^4.2.3" - "@smithy/property-provider" "^4.2.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-node@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.929.0.tgz#83eddbee1a6c6b84ef1dc7b9244898effa1324c1" - integrity sha512-GhNZEacpa7fh8GNggshm5S93UK25bCV5aDK8c2vfe7Y3OxBiL89Ox5GUKCu0xIOqiBdfYkI9wvWCFsQRRn7Bjw== - dependencies: - "@aws-sdk/credential-provider-env" "3.928.0" - "@aws-sdk/credential-provider-http" "3.928.0" - "@aws-sdk/credential-provider-ini" "3.929.0" - "@aws-sdk/credential-provider-process" "3.928.0" - "@aws-sdk/credential-provider-sso" "3.929.0" - "@aws-sdk/credential-provider-web-identity" "3.929.0" - "@aws-sdk/types" "3.922.0" - "@smithy/credential-provider-imds" "^4.2.4" - "@smithy/property-provider" "^4.2.4" - "@smithy/shared-ini-file-loader" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-process@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.916.0.tgz#7c5aa9642a0e1c2a2791d85fe1bedfecae73672e" - integrity sha512-SXDyDvpJ1+WbotZDLJW1lqP6gYGaXfZJrgFSXIuZjHb75fKeNRgPkQX/wZDdUvCwdrscvxmtyJorp2sVYkMcvA== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/property-provider" "^4.2.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-process@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.928.0.tgz#47771efe637d08ae7dd9ece8afbc52d2b0e92f39" - integrity sha512-XL0juran8yhqwn0mreV+NJeHJOkcRBaExsvVn9fXWW37A4gLh4esSJxM2KbSNh0t+/Bk3ehBI5sL9xad+yRDuw== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@smithy/property-provider" "^4.2.4" - "@smithy/shared-ini-file-loader" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-sso@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.916.0.tgz#b99ff591e758a56eefe7b05f1e77efe8f28f8c16" - integrity sha512-gu9D+c+U/Dp1AKBcVxYHNNoZF9uD4wjAKYCjgSN37j4tDsazwMEylbbZLuRNuxfbXtizbo4/TiaxBXDbWM7AkQ== - dependencies: - "@aws-sdk/client-sso" "3.916.0" - "@aws-sdk/core" "3.916.0" - "@aws-sdk/token-providers" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/property-provider" "^4.2.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-sso@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.929.0.tgz#6390e8b3a06c95c1a6301d1658455f2881df4258" - integrity sha512-aADe6cLo4+9MUOe0GnC5kUn8IduEKnTxqBlsciZOplU0/0+Rdp9rRh/e9ZBskeIXZ33eO2HG+KDAf1lvtPT7dA== - dependencies: - "@aws-sdk/client-sso" "3.929.0" - "@aws-sdk/core" "3.928.0" - "@aws-sdk/token-providers" "3.929.0" - "@aws-sdk/types" "3.922.0" - "@smithy/property-provider" "^4.2.4" - "@smithy/shared-ini-file-loader" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-web-identity@3.918.0": - version "3.918.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.918.0.tgz#293c7f4c5148318f04d4cd493ddb3c0cb58017cc" - integrity sha512-qQx5qOhSovVF1EEKTc809WsiKzMqEJrlMSOUycDkE+JMgLPIy2pB2LR1crrIeBGgxFUgFsXHvNHbFjRy+AFBdA== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/nested-clients" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/property-provider" "^4.2.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-web-identity@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.929.0.tgz#588070995f1756fc2818494ed8ae32ed6b6fed6a" - integrity sha512-L18JtW28xUZVTRHblgqZ8QTVGQfxpMLIuVYgQXrVWiY9Iz9EF4XrfZo3ywCAgqfgLi5pgg3fCxx/pe7uiMOs2w== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/nested-clients" "3.929.0" - "@aws-sdk/types" "3.922.0" - "@smithy/property-provider" "^4.2.4" - "@smithy/shared-ini-file-loader" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/lib-storage@^3.837.0": - version "3.918.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/lib-storage/-/lib-storage-3.918.0.tgz#3e356dcf565ebb8d1d76468e79f56696f84830d1" - integrity sha512-AwG+JfRc8YqndPMkEvdNlJG9i113+F7nDpmf1EiruomsmhU3Jxm64r1IdjWiNNnaR/uXiiytx225Pd0IP1/ICg== - dependencies: - "@smithy/abort-controller" "^4.2.3" - "@smithy/middleware-endpoint" "^4.3.5" - "@smithy/smithy-client" "^4.9.1" - buffer "5.6.0" - events "3.3.0" - stream-browserify "3.0.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-bucket-endpoint@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.914.0.tgz#4500425660d45af30e1bb66d8ce9362e040b9c7d" - integrity sha512-mHLsVnPPp4iq3gL2oEBamfpeETFV0qzxRHmcnCfEP3hualV8YF8jbXGmwPCPopUPQDpbYDBHYtXaoClZikCWPQ== - dependencies: - "@aws-sdk/types" "3.914.0" - "@aws-sdk/util-arn-parser" "3.893.0" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - "@smithy/util-config-provider" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-bucket-endpoint@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.922.0.tgz#417efd18e8af948e694c5be751bde6d631138b3d" - integrity sha512-Dpr2YeOaLFqt3q1hocwBesynE3x8/dXZqXZRuzSX/9/VQcwYBFChHAm4mTAl4zuvArtDbLrwzWSxmOWYZGtq5w== - dependencies: - "@aws-sdk/types" "3.922.0" - "@aws-sdk/util-arn-parser" "3.893.0" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/types" "^4.8.1" - "@smithy/util-config-provider" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-expect-continue@3.917.0": - version "3.917.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.917.0.tgz#f0e0cacad99d048c46cdce8f9dbe47351e59a0f5" - integrity sha512-UPBq1ZP2CaxwbncWSbVqkhYXQrmfNiqAtHyBxi413hjRVZ4JhQ1UyH7pz5yqiG8zx2/+Po8cUD4SDUwJgda4nw== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-expect-continue@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.922.0.tgz#02f0b0402fcb8974765b3e7d20f43753bd05738c" - integrity sha512-xmnLWMtmHJHJBupSWMUEW1gyxuRIeQ1Ov2xa8Tqq77fPr4Ft2AluEwiDMaZIMHoAvpxWKEEt9Si59Li7GIA+bQ== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/protocol-http" "^5.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/middleware-flexible-checksums@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.916.0.tgz#ecbec3baf54e79dae04f1fd19f21041482928239" - integrity sha512-CBRRg6slHHBYAm26AWY/pECHK0vVO/peDoNhZiAzUNt4jV6VftotjszEJ904pKGOr7/86CfZxtCnP3CCs3lQjA== - dependencies: - "@aws-crypto/crc32" "5.2.0" - "@aws-crypto/crc32c" "5.2.0" - "@aws-crypto/util" "5.2.0" - "@aws-sdk/core" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/is-array-buffer" "^4.2.0" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-stream" "^4.5.4" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-flexible-checksums@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.928.0.tgz#3e62b563e671fb970b860090283445ed6b8b0608" - integrity sha512-9+aCRt7teItSIMbnGvOY+FhtJnW2ZBUbfD+ug29a/ZbobDfTwmtrmtgEIWdXryFaRbT03HHfaJ3a++lTw4osuw== - dependencies: - "@aws-crypto/crc32" "5.2.0" - "@aws-crypto/crc32c" "5.2.0" - "@aws-crypto/util" "5.2.0" - "@aws-sdk/core" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@smithy/is-array-buffer" "^4.2.0" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/types" "^4.8.1" - "@smithy/util-middleware" "^4.2.4" - "@smithy/util-stream" "^4.5.5" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-host-header@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.914.0.tgz#7e962c3d18c1ecc98606eab09a98dcf1b3402835" - integrity sha512-7r9ToySQ15+iIgXMF/h616PcQStByylVkCshmQqcdeynD/lCn2l667ynckxW4+ql0Q+Bo/URljuhJRxVJzydNA== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-host-header@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.922.0.tgz#f19621fd19764f7eb0a33795ce0f43402080e394" - integrity sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/protocol-http" "^5.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/middleware-location-constraint@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.914.0.tgz#ee877bdaa54746f65919fa54685ef392256bfb19" - integrity sha512-Mpd0Sm9+GN7TBqGnZg1+dO5QZ/EOYEcDTo7KfvoyrXScMlxvYm9fdrUVMmLdPn/lntweZGV3uNrs+huasGOOTA== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-location-constraint@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.922.0.tgz#c455d40e3ab49014a1193fbcb2bf29885d345b7c" - integrity sha512-T4iqd7WQ2DDjCH/0s50mnhdoX+IJns83ZE+3zj9IDlpU0N2aq8R91IG890qTfYkUEdP9yRm0xir/CNed+v6Dew== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/middleware-logger@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.914.0.tgz#222d50ec69447715d6954eb6db0029f11576227b" - integrity sha512-/gaW2VENS5vKvJbcE1umV4Ag3NuiVzpsANxtrqISxT3ovyro29o1RezW/Avz/6oJqjnmgz8soe9J1t65jJdiNg== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-logger@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.922.0.tgz#3a43e2b7ec72b043751a7fd45f0514db77756be9" - integrity sha512-AkvYO6b80FBm5/kk2E636zNNcNgjztNNUxpqVx+huyGn9ZqGTzS4kLqW2hO6CBe5APzVtPCtiQsXL24nzuOlAg== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/middleware-recursion-detection@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.914.0.tgz#bf65759cf303f271b22770e7f9675034b4ced946" - integrity sha512-yiAjQKs5S2JKYc+GrkvGMwkUvhepXDigEXpSJqUseR/IrqHhvGNuOxDxq+8LbDhM4ajEW81wkiBbU+Jl9G82yQ== - dependencies: - "@aws-sdk/types" "3.914.0" - "@aws/lambda-invoke-store" "^0.0.1" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-recursion-detection@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.922.0.tgz#cca89bd926ad05893f9b99b253fa50a6b6c7b829" - integrity sha512-TtSCEDonV/9R0VhVlCpxZbp/9sxQvTTRKzIf8LxW3uXpby6Wl8IxEciBJlxmSkoqxh542WRcko7NYODlvL/gDA== - dependencies: - "@aws-sdk/types" "3.922.0" - "@aws/lambda-invoke-store" "^0.1.1" - "@smithy/protocol-http" "^5.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/middleware-sdk-s3@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.916.0.tgz#5c1cc4645186b3c0f7ac5f6a897885af0b62198e" - integrity sha512-pjmzzjkEkpJObzmTthqJPq/P13KoNFuEi/x5PISlzJtHofCNcyXeVAQ90yvY2dQ6UXHf511Rh1/ytiKy2A8M0g== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@aws-sdk/util-arn-parser" "3.893.0" - "@smithy/core" "^3.17.1" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/signature-v4" "^5.3.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - "@smithy/util-config-provider" "^4.2.0" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-stream" "^4.5.4" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-sdk-s3@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.928.0.tgz#576fe6763ad5065cdc839a32f160210d96e8d337" - integrity sha512-LTkjS6cpJ2PEtsottTKq7JxZV0oH+QJ12P/dGNPZL4URayjEMBVR/dp4zh835X/FPXzijga3sdotlIKzuFy9FA== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@aws-sdk/util-arn-parser" "3.893.0" - "@smithy/core" "^3.17.2" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/signature-v4" "^5.3.4" - "@smithy/smithy-client" "^4.9.2" - "@smithy/types" "^4.8.1" - "@smithy/util-config-provider" "^4.2.0" - "@smithy/util-middleware" "^4.2.4" - "@smithy/util-stream" "^4.5.5" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-ssec@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.914.0.tgz#4042dfed7a4d4234e37a84bab9d1cd9998a22180" - integrity sha512-V1Oae/oLVbpNb9uWs+v80GKylZCdsbqs2c2Xb1FsAUPtYeSnxFuAWsF3/2AEMSSpFe0dTC5KyWr/eKl2aim9VQ== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-ssec@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.922.0.tgz#1c56b2619cdd604e97203148030f299980494008" - integrity sha512-eHvSJZTSRJO+/tjjGD6ocnPc8q9o3m26+qbwQTu/4V6yOJQ1q+xkDZNqwJQphL+CodYaQ7uljp8g1Ji/AN3D9w== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/middleware-user-agent@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.916.0.tgz#a0894ae6d70d7a81b2572ee69ed0d3049d39dfce" - integrity sha512-mzF5AdrpQXc2SOmAoaQeHpDFsK2GE6EGcEACeNuoESluPI2uYMpuuNMYrUufdnIAIyqgKlis0NVxiahA5jG42w== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@aws-sdk/util-endpoints" "3.916.0" - "@smithy/core" "^3.17.1" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-user-agent@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.928.0.tgz#51fb98b44849712fe01e655182c9b9d9cb1d9630" - integrity sha512-ESvcfLx5PtpdUM3ptCwb80toBTd3y5I4w5jaeOPHihiZr7jkRLE/nsaCKzlqscPs6UQ8xI0maav04JUiTskcHw== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@aws-sdk/util-endpoints" "3.922.0" - "@smithy/core" "^3.17.2" - "@smithy/protocol-http" "^5.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/nested-clients@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.916.0.tgz#2f79b924dd6c25cc3c40f6a0453097ae7a512702" - integrity sha512-tgg8e8AnVAer0rcgeWucFJ/uNN67TbTiDHfD+zIOPKep0Z61mrHEoeT/X8WxGIOkEn4W6nMpmS4ii8P42rNtnA== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.916.0" - "@aws-sdk/middleware-host-header" "3.914.0" - "@aws-sdk/middleware-logger" "3.914.0" - "@aws-sdk/middleware-recursion-detection" "3.914.0" - "@aws-sdk/middleware-user-agent" "3.916.0" - "@aws-sdk/region-config-resolver" "3.914.0" - "@aws-sdk/types" "3.914.0" - "@aws-sdk/util-endpoints" "3.916.0" - "@aws-sdk/util-user-agent-browser" "3.914.0" - "@aws-sdk/util-user-agent-node" "3.916.0" - "@smithy/config-resolver" "^4.4.0" - "@smithy/core" "^3.17.1" - "@smithy/fetch-http-handler" "^5.3.4" - "@smithy/hash-node" "^4.2.3" - "@smithy/invalid-dependency" "^4.2.3" - "@smithy/middleware-content-length" "^4.2.3" - "@smithy/middleware-endpoint" "^4.3.5" - "@smithy/middleware-retry" "^4.4.5" - "@smithy/middleware-serde" "^4.2.3" - "@smithy/middleware-stack" "^4.2.3" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/node-http-handler" "^4.4.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - "@smithy/url-parser" "^4.2.3" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-body-length-node" "^4.2.1" - "@smithy/util-defaults-mode-browser" "^4.3.4" - "@smithy/util-defaults-mode-node" "^4.2.6" - "@smithy/util-endpoints" "^3.2.3" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-retry" "^4.2.3" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/nested-clients@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.929.0.tgz#e8781115e6e237021e54830a7febcef840d808cc" - integrity sha512-emR4LTSupxPed1ni0zVxz5msezz/gA1YYXooiW567+NyhvLgSzDvNjK7GPU1waLCj1LrRFe7NkXX1pwa5sPrpw== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.928.0" - "@aws-sdk/middleware-host-header" "3.922.0" - "@aws-sdk/middleware-logger" "3.922.0" - "@aws-sdk/middleware-recursion-detection" "3.922.0" - "@aws-sdk/middleware-user-agent" "3.928.0" - "@aws-sdk/region-config-resolver" "3.925.0" - "@aws-sdk/types" "3.922.0" - "@aws-sdk/util-endpoints" "3.922.0" - "@aws-sdk/util-user-agent-browser" "3.922.0" - "@aws-sdk/util-user-agent-node" "3.928.0" - "@smithy/config-resolver" "^4.4.2" - "@smithy/core" "^3.17.2" - "@smithy/fetch-http-handler" "^5.3.5" - "@smithy/hash-node" "^4.2.4" - "@smithy/invalid-dependency" "^4.2.4" - "@smithy/middleware-content-length" "^4.2.4" - "@smithy/middleware-endpoint" "^4.3.6" - "@smithy/middleware-retry" "^4.4.6" - "@smithy/middleware-serde" "^4.2.4" - "@smithy/middleware-stack" "^4.2.4" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/node-http-handler" "^4.4.4" - "@smithy/protocol-http" "^5.3.4" - "@smithy/smithy-client" "^4.9.2" - "@smithy/types" "^4.8.1" - "@smithy/url-parser" "^4.2.4" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-body-length-node" "^4.2.1" - "@smithy/util-defaults-mode-browser" "^4.3.5" - "@smithy/util-defaults-mode-node" "^4.2.8" - "@smithy/util-endpoints" "^3.2.4" - "@smithy/util-middleware" "^4.2.4" - "@smithy/util-retry" "^4.2.4" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/region-config-resolver@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.914.0.tgz#b6d2825081195ce1c634b8c92b1e19b08f140008" - integrity sha512-KlmHhRbn1qdwXUdsdrJ7S/MAkkC1jLpQ11n+XvxUUUCGAJd1gjC7AjxPZUM7ieQ2zcb8bfEzIU7al+Q3ZT0u7Q== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/config-resolver" "^4.4.0" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/region-config-resolver@3.925.0": - version "3.925.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.925.0.tgz#789fab5b277ec21753b908c78cee18bd70998475" - integrity sha512-FOthcdF9oDb1pfQBRCfWPZhJZT5wqpvdAS5aJzB1WDZ+6EuaAhLzLH/fW1slDunIqq1PSQGG3uSnVglVVOvPHQ== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/config-resolver" "^4.4.2" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/s3-presigned-post@^3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.929.0.tgz#f53e14a8dcd464e0c5dc795b9c1bd52a7c583b20" - integrity sha512-AtsPrNZfAYIn7SNUjU9q2L9gI/lJ6ycmGfj6JP6vZAFq6HURWFkM2Mh5Sdp6fmIFE3S4UtnE05ixDpd5IlQz/Q== - dependencies: - "@aws-sdk/client-s3" "3.929.0" - "@aws-sdk/types" "3.922.0" - "@aws-sdk/util-format-url" "3.922.0" - "@smithy/middleware-endpoint" "^4.3.6" - "@smithy/signature-v4" "^5.3.4" - "@smithy/types" "^4.8.1" - "@smithy/util-hex-encoding" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@aws-sdk/s3-request-presigner@^3.828.0": - version "3.918.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.918.0.tgz#4a67369f5b940a4e1b4b015a30e2149c9bc1fb20" - integrity sha512-s7aBuDNjVr7EcSEHcCkpWCow90BMUlj9LgPXRmO7oAZVnb/7cMl+x9ucAn3lWRttIXqBCIIPK49EaIyZNfjWDg== - dependencies: - "@aws-sdk/signature-v4-multi-region" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@aws-sdk/util-format-url" "3.914.0" - "@smithy/middleware-endpoint" "^4.3.5" - "@smithy/protocol-http" "^5.3.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/signature-v4-multi-region@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.916.0.tgz#d70e3dc9ca2cb3f65923283600a0a6e9a6c4ec7f" - integrity sha512-fuzUMo6xU7e0NBzBA6TQ4FUf1gqNbg4woBSvYfxRRsIfKmSMn9/elXXn4sAE5UKvlwVQmYnb6p7dpVRPyFvnQA== - dependencies: - "@aws-sdk/middleware-sdk-s3" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/protocol-http" "^5.3.3" - "@smithy/signature-v4" "^5.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/signature-v4-multi-region@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.928.0.tgz#4de26dbdfcc9a8db536c4e4ed6367728a37e0a64" - integrity sha512-1+Ic8+MyqQy+OE6QDoQKVCIcSZO+ETmLLLpVS5yu0fihBU85B5HHU7iaKX1qX7lEaGPMpSN/mbHW0VpyQ0Xqaw== - dependencies: - "@aws-sdk/middleware-sdk-s3" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@smithy/protocol-http" "^5.3.4" - "@smithy/signature-v4" "^5.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/token-providers@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.916.0.tgz#e824fd44a553c4047b769caf22a94fd2705c9f1d" - integrity sha512-13GGOEgq5etbXulFCmYqhWtpcEQ6WI6U53dvXbheW0guut8fDFJZmEv7tKMTJgiybxh7JHd0rWcL9JQND8DwoQ== - dependencies: - "@aws-sdk/core" "3.916.0" - "@aws-sdk/nested-clients" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/property-provider" "^4.2.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/token-providers@3.929.0": - version "3.929.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.929.0.tgz#cc15bf597ad499f2e639a8ef15daeee169a2951f" - integrity sha512-78kph1R6TVJ53VXDKUmt64HMqWjTECLymJ7kLguz2QJiWh2ZdLvpyYGvaueEwwhisHYBh2qef1tGIf/PpEb8SQ== - dependencies: - "@aws-sdk/core" "3.928.0" - "@aws-sdk/nested-clients" "3.929.0" - "@aws-sdk/types" "3.922.0" - "@smithy/property-provider" "^4.2.4" - "@smithy/shared-ini-file-loader" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/types@3.914.0", "@aws-sdk/types@^3.222.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.914.0.tgz#175cf9a4b2267aafbb110fe1316e6827de951fdb" - integrity sha512-kQWPsRDmom4yvAfyG6L1lMmlwnTzm1XwMHOU+G5IFlsP4YEaMtXidDzW/wiivY0QFrhfCz/4TVmu0a2aPU57ug== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/types@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.922.0.tgz#e92daf55272171caac8dba9d425786646466d935" - integrity sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w== - dependencies: - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/util-arn-parser@3.893.0": - version "3.893.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz#fcc9b792744b9da597662891c2422dda83881d8d" - integrity sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA== - dependencies: - tslib "^2.6.2" - -"@aws-sdk/util-endpoints@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.916.0.tgz#ab54249b8090cd66fe14aa8518097107a2595196" - integrity sha512-bAgUQwvixdsiGNcuZSDAOWbyHlnPtg8G8TyHD6DTfTmKTHUW6tAn+af/ZYJPXEzXhhpwgJqi58vWnsiDhmr7NQ== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/types" "^4.8.0" - "@smithy/url-parser" "^4.2.3" - "@smithy/util-endpoints" "^3.2.3" - tslib "^2.6.2" - -"@aws-sdk/util-endpoints@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.922.0.tgz#817457d6a78ce366bdb7b201c638dba5ffdbfe60" - integrity sha512-4ZdQCSuNMY8HMlR1YN4MRDdXuKd+uQTeKIr5/pIM+g3TjInZoj8imvXudjcrFGA63UF3t92YVTkBq88mg58RXQ== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/types" "^4.8.1" - "@smithy/url-parser" "^4.2.4" - "@smithy/util-endpoints" "^3.2.4" - tslib "^2.6.2" - -"@aws-sdk/util-format-url@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.914.0.tgz#6592dd713faa311200fc9ae9295a79618f33e2ca" - integrity sha512-QpdkoQjvPaYyzZwgk41vFyHQM5s0DsrsbQ8IoPUggQt4HaJUvmL1ShwMcSldbgdzwiRMqXUK8q7jrqUvkYkY6w== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/querystring-builder" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/util-format-url@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.922.0.tgz#a1b91b180d75a73dd8eedee0913a60153673392e" - integrity sha512-UYLWPvZEd6TYilNkrQrIeXh2bXZsY3ighYErSEjD24f3JQhg0XdXoR/QHIE8licHu2qFrTRM6yi9LH1GY6X0cg== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/querystring-builder" "^4.2.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/util-locate-window@^3.0.0": - version "3.893.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz#5df15f24e1edbe12ff1fe8906f823b51cd53bae8" - integrity sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg== - dependencies: - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-browser@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.914.0.tgz#ed29fd87f6ffba6f53615894a5e969cb9013af59" - integrity sha512-rMQUrM1ECH4kmIwlGl9UB0BtbHy6ZuKdWFrIknu8yGTRI/saAucqNTh5EI1vWBxZ0ElhK5+g7zOnUuhSmVQYUA== - dependencies: - "@aws-sdk/types" "3.914.0" - "@smithy/types" "^4.8.0" - bowser "^2.11.0" - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-browser@3.922.0": - version "3.922.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.922.0.tgz#734bbe74d34c3fbdb96aca80a151d3d7e7e87c30" - integrity sha512-qOJAERZ3Plj1st7M4Q5henl5FRpE30uLm6L9edZqZXGR6c7ry9jzexWamWVpQ4H4xVAVmiO9dIEBAfbq4mduOA== - dependencies: - "@aws-sdk/types" "3.922.0" - "@smithy/types" "^4.8.1" - bowser "^2.11.0" - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-node@3.916.0": - version "3.916.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.916.0.tgz#3ab5fdb9f45345f19f426941ece71988b31bf58d" - integrity sha512-CwfWV2ch6UdjuSV75ZU99N03seEUb31FIUrXBnwa6oONqj/xqXwrxtlUMLx6WH3OJEE4zI3zt5PjlTdGcVwf4g== - dependencies: - "@aws-sdk/middleware-user-agent" "3.916.0" - "@aws-sdk/types" "3.914.0" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-node@3.928.0": - version "3.928.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.928.0.tgz#adcd93ae10d484e6c172369d6140ec6d09a2eb5c" - integrity sha512-s0jP67nQLLWVWfBtqTkZUkSWK5e6OI+rs+wFya2h9VLyWBFir17XSDI891s8HZKIVCEl8eBrup+hhywm4nsIAA== - dependencies: - "@aws-sdk/middleware-user-agent" "3.928.0" - "@aws-sdk/types" "3.922.0" - "@smithy/node-config-provider" "^4.3.4" - "@smithy/types" "^4.8.1" - tslib "^2.6.2" - -"@aws-sdk/xml-builder@3.914.0": - version "3.914.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.914.0.tgz#4e98b479856113db877d055e7b008065c50266d4" - integrity sha512-k75evsBD5TcIjedycYS7QXQ98AmOtbnxRJOPtCo0IwYRmy7UvqgS/gBL5SmrIqeV6FDSYRQMgdBxSMp6MLmdew== - dependencies: - "@smithy/types" "^4.8.0" - fast-xml-parser "5.2.5" - tslib "^2.6.2" - -"@aws-sdk/xml-builder@3.921.0": - version "3.921.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.921.0.tgz#e4d4d21b09341648b598d720c602ee76d7a84594" - integrity sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q== - dependencies: - "@smithy/types" "^4.8.1" - fast-xml-parser "5.2.5" - tslib "^2.6.2" - -"@aws/lambda-invoke-store@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz#92d792a7dda250dfcb902e13228f37a81be57c8f" - integrity sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw== - -"@aws/lambda-invoke-store@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.1.1.tgz#2e67f17040b930bde00a79ffb484eb9e77472b06" - integrity sha512-RcLam17LdlbSOSp9VxmUu1eI6Mwxp+OwhD2QhiSNmNCzoDb0EeUXTD2n/WbcnrAYMGlmf05th6QYq23VqvJqpA== - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== - dependencies: - "@babel/helper-validator-identifier" "^7.27.1" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/compat-data@^7.27.2": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" - integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== - -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" - integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-module-transforms" "^7.28.3" - "@babel/helpers" "^7.28.4" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.5" - "@babel/types" "^7.28.5" - "@jridgewell/remapping" "^2.3.5" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.28.5", "@babel/generator@^7.7.2": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" - integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== - dependencies: - "@babel/parser" "^7.28.5" - "@babel/types" "^7.28.5" - "@jridgewell/gen-mapping" "^0.3.12" - "@jridgewell/trace-mapping" "^0.3.28" - jsesc "^3.0.2" - -"@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== - dependencies: - "@babel/compat-data" "^7.27.2" - "@babel/helper-validator-option" "^7.27.1" - browserslist "^4.24.0" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-globals@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" - integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== - -"@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/helper-module-transforms@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" - integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== - dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.28.3" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== - -"@babel/helper-string-parser@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== - -"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" - integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== - -"@babel/helper-validator-option@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" - integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== - -"@babel/helpers@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" - integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== - dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.4" - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5", "@babel/parser@^7.7.0": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" - integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== - dependencies: - "@babel/types" "^7.28.5" - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-import-attributes@^7.24.7": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" - integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-syntax-import-meta@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.7.2": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" - integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" - integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/template@^7.27.2", "@babel/template@^7.3.3": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" - -"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.5", "@babel/traverse@^7.7.0": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" - integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.5" - debug "^4.3.1" - -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5", "@babel/types@^7.3.3", "@babel/types@^7.7.0": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" - integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== - dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.28.5" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@colors/colors@1.6.0", "@colors/colors@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" - integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== - -"@dabh/diagnostics@^2.0.8": - version "2.0.8" - resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.8.tgz#ead97e72ca312cf0e6dd7af0d300b58993a31a5e" - integrity sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q== - dependencies: - "@so-ric/colorspace" "^1.1.6" - enabled "2.0.x" - kuler "^2.0.0" - -"@eslint-community/eslint-utils@^4.2.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" - integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== - dependencies: - eslint-visitor-keys "^3.4.3" - -"@eslint-community/regexpp@^4.6.1": - version "4.12.2" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" - integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== - -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== - dependencies: - 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" - -"@eslint/js@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" - integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== - -"@faker-js/faker@5.5.3": - version "5.5.3" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-5.5.3.tgz#18e3af6b8eae7984072bbeb0c0858474d7c4cefe" - integrity sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw== - -"@faker-js/faker@^8.4.1": - version "8.4.1" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-8.4.1.tgz#5d5e8aee8fce48f5e189bf730ebd1f758f491451" - integrity sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg== - -"@graphql-tools/graphql-file-loader@^8.0.19": - version "8.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.2.tgz#d329514cdb3179b633e4139bd27add40f87bb038" - integrity sha512-VB6ttpwkqCu0KsA1/Wmev4qsu05Qfw49kgVSKkPjuyDQfVaqtr9ewEQRkX5CqnqHGEeLl6sOlNGEMM5fCVMWGQ== - dependencies: - "@graphql-tools/import" "7.1.2" - "@graphql-tools/utils" "^10.9.1" - globby "^11.0.3" - tslib "^2.4.0" - unixify "^1.0.0" - -"@graphql-tools/import@7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.2.tgz#b8e623786a2662d21d969da986902cd1f317c5a9" - integrity sha512-+tlNQbLEqAA4LdWoLwM1tckx95lo8WIKd8vhj99b9rLwN/KfLwHWzdS3jnUFK7+99vmHmN1oE5v5zmqJz0MTKw== - dependencies: - "@graphql-tools/utils" "^10.9.1" - "@theguild/federation-composition" "^0.20.1" - resolve-from "5.0.0" - tslib "^2.4.0" - -"@graphql-tools/load@^8.0.19": - version "8.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.2.tgz#8d13412b897735b8ad5a8c8e1396b4ae5fc46190" - integrity sha512-WhDPv25/jRND+0uripofMX0IEwo6mrv+tJg6HifRmDu8USCD7nZhufT0PP7lIcuutqjIQFyogqT70BQsy6wOgw== - dependencies: - "@graphql-tools/schema" "^10.0.25" - "@graphql-tools/utils" "^10.9.1" - p-limit "3.1.0" - tslib "^2.4.0" - -"@graphql-tools/merge@8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.1.tgz#06121942ad28982a14635dbc87b5d488a041d722" - integrity sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg== - dependencies: - "@graphql-tools/utils" "8.9.0" - tslib "^2.4.0" - -"@graphql-tools/merge@^8.4.1": - version "8.4.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.2.tgz#95778bbe26b635e8d2f60ce9856b388f11fe8288" - integrity sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw== - dependencies: - "@graphql-tools/utils" "^9.2.1" - tslib "^2.4.0" - -"@graphql-tools/merge@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.1.tgz#6a51775f5ba6bdef213def1fd277536f9468b44c" - integrity sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w== - dependencies: - "@graphql-tools/utils" "^10.9.1" - tslib "^2.4.0" - -"@graphql-tools/mock@^8.1.2": - version "8.7.20" - resolved "https://registry.yarnpkg.com/@graphql-tools/mock/-/mock-8.7.20.tgz#c83ae0f1940d194a3982120c9c85f3ac6b4f7f20" - integrity sha512-ljcHSJWjC/ZyzpXd5cfNhPI7YljRVvabKHPzKjEs5ElxWu2cdlLGvyNYepApXDsM/OJG/2xuhGM+9GWu5gEAPQ== - dependencies: - "@graphql-tools/schema" "^9.0.18" - "@graphql-tools/utils" "^9.2.1" - fast-json-stable-stringify "^2.1.0" - tslib "^2.4.0" - -"@graphql-tools/schema@^10.0.23", "@graphql-tools/schema@^10.0.25": - version "10.0.25" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.25.tgz#73de08bc765f482cde334562a1bebe95a2e5a3c4" - integrity sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw== - dependencies: - "@graphql-tools/merge" "^9.1.1" - "@graphql-tools/utils" "^10.9.1" - tslib "^2.4.0" - -"@graphql-tools/schema@^8.0.0": - version "8.5.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.5.1.tgz#c2f2ff1448380919a330312399c9471db2580b58" - integrity sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg== - dependencies: - "@graphql-tools/merge" "8.3.1" - "@graphql-tools/utils" "8.9.0" - tslib "^2.4.0" - value-or-promise "1.0.11" - -"@graphql-tools/schema@^9.0.18": - version "9.0.19" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.19.tgz#c4ad373b5e1b8a0cf365163435b7d236ebdd06e7" - integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== - dependencies: - "@graphql-tools/merge" "^8.4.1" - "@graphql-tools/utils" "^9.2.1" - tslib "^2.4.0" - value-or-promise "^1.0.12" - -"@graphql-tools/utils@8.9.0": - version "8.9.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.9.0.tgz#c6aa5f651c9c99e1aca55510af21b56ec296cdb7" - integrity sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg== - dependencies: - tslib "^2.4.0" - -"@graphql-tools/utils@^10.9.1": - version "10.9.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.9.1.tgz#ff4067256f2080db0c66f4475858f29a8da65ecf" - integrity sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - "@whatwg-node/promise-helpers" "^1.0.0" - cross-inspect "1.0.1" - dset "^3.1.4" - tslib "^2.4.0" - -"@graphql-tools/utils@^9.2.1": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" - integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - tslib "^2.4.0" - -"@graphql-typed-document-node/core@^3.1.1": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" - integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== - -"@grpc/grpc-js@^1.13.2": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.14.0.tgz#a3c47e7816ca2b4d5490cba9e06a3cf324e675ad" - integrity sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg== - dependencies: - "@grpc/proto-loader" "^0.8.0" - "@js-sdsl/ordered-map" "^4.4.2" - -"@grpc/proto-loader@^0.7.5": - version "0.7.15" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.15.tgz#4cdfbf35a35461fc843abe8b9e2c0770b5095e60" - integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== - dependencies: - lodash.camelcase "^4.3.0" - long "^5.0.0" - protobufjs "^7.2.5" - yargs "^17.7.2" - -"@grpc/proto-loader@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.8.0.tgz#b6c324dd909c458a0e4aa9bfd3d69cf78a4b9bd8" - integrity sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ== - dependencies: - lodash.camelcase "^4.3.0" - long "^5.0.0" - protobufjs "^7.5.3" - yargs "^17.7.2" - -"@humanwhocodes/config-array@^0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" - integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== - dependencies: - "@humanwhocodes/object-schema" "^2.0.3" - debug "^4.3.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" - integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== - -"@ioredis/commands@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.4.0.tgz#9f657d51cdd5d2fdb8889592aa4a355546151f25" - integrity sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - 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" - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - 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" - -"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" - integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - -"@jest/core@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" - integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== - dependencies: - "@jest/console" "^29.7.0" - "@jest/reporters" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.7.0" - jest-config "^29.7.0" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-resolve-dependencies "^29.7.0" - jest-runner "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - jest-watcher "^29.7.0" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" - integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== - dependencies: - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== - dependencies: - jest-get-type "^29.6.3" - -"@jest/expect@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" - integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== - dependencies: - expect "^29.7.0" - jest-snapshot "^29.7.0" - -"@jest/fake-timers@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== - dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -"@jest/globals@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" - integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/types" "^29.6.3" - jest-mock "^29.7.0" - -"@jest/reporters@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" - integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^6.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - jest-worker "^29.7.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - -"@jest/source-map@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" - integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.18" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" - integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== - dependencies: - "@jest/console" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" - integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== - dependencies: - "@jest/test-result" "^29.7.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - slash "^3.0.0" - -"@jest/transform@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" - integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.2" - -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@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" - -"@josephg/resolvable@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@josephg/resolvable/-/resolvable-1.0.1.tgz#69bc4db754d79e1a2f17a650d3466e038d94a5eb" - integrity sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg== - -"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" - integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/remapping@^2.3.5": - version "2.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" - integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" - integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== - -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.31" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" - integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@js-sdsl/ordered-map@^4.4.2": - version "4.4.2" - resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" - integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== - -"@keycloak/keycloak-admin-client@26.2.4": - version "26.2.4" - resolved "https://registry.yarnpkg.com/@keycloak/keycloak-admin-client/-/keycloak-admin-client-26.2.4.tgz#9484ad8e7fb1bcc5fbd540211b77d16ade3792a2" - integrity sha512-2N56jps2HEGJoslxT2LQudUNEKmVwXyfZushR77hkoQF05YQ2USFOvqeM0kxxFb/3S38ZwS4wZv0NiZKnAwhUw== - dependencies: - camelize-ts "^3.0.0" - url-join "^5.0.0" - url-template "^3.1.1" - -"@messageformat/core@^3.2.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.4.0.tgz#2814c23383dec7bddf535d54f2a03e410165ca9f" - integrity sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw== - dependencies: - "@messageformat/date-skeleton" "^1.0.0" - "@messageformat/number-skeleton" "^1.0.0" - "@messageformat/parser" "^5.1.0" - "@messageformat/runtime" "^3.0.1" - make-plural "^7.0.0" - safe-identifier "^0.4.1" - -"@messageformat/date-skeleton@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz#3bad068cbf5873d14592cfc7a73dd4d8615e2739" - integrity sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A== - -"@messageformat/number-skeleton@^1.0.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz#e7c245c41a1b2722bc59dad68f4d454f761bc9b4" - integrity sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg== - -"@messageformat/parser@^5.1.0": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.1.tgz#ca7d6c18e9f3f6b6bc984a465dac16da00106055" - integrity sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg== - dependencies: - moo "^0.5.1" - -"@messageformat/runtime@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@messageformat/runtime/-/runtime-3.0.1.tgz#94d1f6c43265c28ef7aed98ecfcc0968c6c849ac" - integrity sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg== - dependencies: - make-plural "^7.0.0" - -"@newrelic/fn-inspect@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@newrelic/fn-inspect/-/fn-inspect-4.4.0.tgz#f1884812186854704cb276501fabd9e76fabcef7" - integrity sha512-VgoXZp3zqP1167XvrA772EHDFUNuYGQh14whFq1d2sE6dC3ZL46tXI9JY0yZdAAeyCERi7mhq7CPx9BYiTOl/A== - dependencies: - nan "^2.22.2" - node-gyp-build "^4.8.1" - prebuildify "^6.0.1" - -"@newrelic/native-metrics@^11.1.0": - version "11.1.0" - resolved "https://registry.yarnpkg.com/@newrelic/native-metrics/-/native-metrics-11.1.0.tgz#6a42005dce61a2ff8ba1cae8978cfc98c085a698" - integrity sha512-V+FRgRgv6R8LAnVsix8d1bL5SMmBONURaT/6F//rOgycvU7PfPoVEocacpSRzPFxMI27e46iAf3W5fGosbQ8yQ== - dependencies: - nan "^2.22.2" - node-gyp-build "^4.8.1" - prebuildify "^6.0.1" - -"@newrelic/security-agent@^2.4.2": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@newrelic/security-agent/-/security-agent-2.4.4.tgz#2a813573e6c31659eee3a11b618abfa9e9db162d" - integrity sha512-KQs76SGqTOG/pTuqRGwzy1lNAm96z8gbb5XHl9FsbOY7Kt9Ltbr46AiiovDHhNQbXFTPnHEY7FfmNJ4GK4W5dQ== - dependencies: - axios "^1.12.0" - check-disk-space "^3.4.0" - content-type "^1.0.5" - cron "^3.1.7" - fast-safe-stringify "^2.1.1" - find-package-json "^1.2.0" - hash.js "^1.1.7" - html-entities "^2.3.6" - https-proxy-agent "^7.0.4" - is-invalid-path "^1.0.2" - js-yaml "^4.1.0" - jsonschema "^1.4.1" - lodash "^4.17.21" - log4js "^6.9.1" - pretty-bytes "^5.6.0" - request-ip "^3.3.0" - ringbufferjs "^2.0.0" - semver "^7.5.4" - unescape "^1.0.1" - unescape-js "^1.1.4" - uuid "^9.0.1" - ws "^8.17.1" - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@opentelemetry/api-logs@0.201.1": - version "0.201.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.201.1.tgz#deb39d5c7e6bf08b1e8ede3d9be14629c9cc6314" - integrity sha512-IxcFDP1IGMDemVFG2by/AMK+/o6EuBQ8idUq3xZ6MxgQGeumYZuX5OwR0h9HuvcUc/JPjQGfU5OHKIKYDJcXeA== - dependencies: - "@opentelemetry/api" "^1.3.0" - -"@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" - integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== - -"@opentelemetry/core@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.0.1.tgz#44e1149d5666a4743cde943ef89841db3ce0f8bc" - integrity sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw== - dependencies: - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/core@2.2.0", "@opentelemetry/core@^2.0.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.2.0.tgz#2f857d7790ff160a97db3820889b5f4cade6eaee" - integrity sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw== - dependencies: - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/exporter-metrics-otlp-http@0.201.1": - version "0.201.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.201.1.tgz#3d173962b99131b15cfa659dc6f858264ecb287d" - integrity sha512-LMRVg2yTev28L51RLLUK3gY0avMa1RVBq7IkYNtXDBxJRcd0TGGq/0rqfk7Y4UIM9NCJhDIUFHeGg8NpSgSWcw== - dependencies: - "@opentelemetry/core" "2.0.1" - "@opentelemetry/otlp-exporter-base" "0.201.1" - "@opentelemetry/otlp-transformer" "0.201.1" - "@opentelemetry/resources" "2.0.1" - "@opentelemetry/sdk-metrics" "2.0.1" - -"@opentelemetry/exporter-metrics-otlp-proto@^0.201.1": - version "0.201.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.201.1.tgz#5fbf09fc66eb7ffa3dda5f6c18619ca75b5cfcbe" - integrity sha512-9ie2jcaUQZdIoe6B02r0rF4Gz+JsZ9mev/2pYou1N0woOUkFM8xwO6BAlORnrFVslqF/XO5WG3q5FsTbuC5iiw== - dependencies: - "@opentelemetry/core" "2.0.1" - "@opentelemetry/exporter-metrics-otlp-http" "0.201.1" - "@opentelemetry/otlp-exporter-base" "0.201.1" - "@opentelemetry/otlp-transformer" "0.201.1" - "@opentelemetry/resources" "2.0.1" - "@opentelemetry/sdk-metrics" "2.0.1" - -"@opentelemetry/otlp-exporter-base@0.201.1": - version "0.201.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.201.1.tgz#2d75597024e47459b5c5e7fa24934969820ef040" - integrity sha512-FiS/mIWmZXyRxYGyXPHY+I/4+XrYVTD7Fz/zwOHkVPQsA1JTakAOP9fAi6trXMio0dIpzvQujLNiBqGM7ExrQw== - dependencies: - "@opentelemetry/core" "2.0.1" - "@opentelemetry/otlp-transformer" "0.201.1" - -"@opentelemetry/otlp-transformer@0.201.1": - version "0.201.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.201.1.tgz#7eef57834a33b83e4ccab320f88370e8b085a235" - integrity sha512-+q/8Yuhtu9QxCcjEAXEO8fXLjlSnrnVwfzi9jiWaMAppQp69MoagHHomQj02V2WnGjvBod5ajgkbK4IoWab50A== - dependencies: - "@opentelemetry/api-logs" "0.201.1" - "@opentelemetry/core" "2.0.1" - "@opentelemetry/resources" "2.0.1" - "@opentelemetry/sdk-logs" "0.201.1" - "@opentelemetry/sdk-metrics" "2.0.1" - "@opentelemetry/sdk-trace-base" "2.0.1" - protobufjs "^7.3.0" - -"@opentelemetry/resources@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.0.1.tgz#0365d134291c0ed18d96444a1e21d0e6a481c840" - integrity sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw== - dependencies: - "@opentelemetry/core" "2.0.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/resources@2.2.0", "@opentelemetry/resources@^2.0.1": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.2.0.tgz#b90a950ad98551295b76ea8a0e7efe45a179badf" - integrity sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A== - dependencies: - "@opentelemetry/core" "2.2.0" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/sdk-logs@0.201.1": - version "0.201.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.201.1.tgz#dbf4c68ddcdb7907647e31e19e510336b04df102" - integrity sha512-Ug8gtpssUNUnfpotB9ZhnSsPSGDu+7LngTMgKl31mmVJwLAKyl6jC8diZrMcGkSgBh0o5dbg9puvLyR25buZfw== - dependencies: - "@opentelemetry/api-logs" "0.201.1" - "@opentelemetry/core" "2.0.1" - "@opentelemetry/resources" "2.0.1" - -"@opentelemetry/sdk-metrics@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz#efb6e9349e8a9038ac622e172692bfcdcad8010b" - integrity sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g== - dependencies: - "@opentelemetry/core" "2.0.1" - "@opentelemetry/resources" "2.0.1" - -"@opentelemetry/sdk-metrics@^2.0.1": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz#3824133f0d681d778aff0f52b02a87ec6750fc2d" - integrity sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw== - dependencies: - "@opentelemetry/core" "2.2.0" - "@opentelemetry/resources" "2.2.0" - -"@opentelemetry/sdk-trace-base@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz#25808bb6a3d08a501ad840249e4d43d3493eb6e5" - integrity sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ== - dependencies: - "@opentelemetry/core" "2.0.1" - "@opentelemetry/resources" "2.0.1" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/sdk-trace-base@^2.0.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz#ddef9a0afd01a623d8625a3529f2137b05e67d0b" - integrity sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw== - dependencies: - "@opentelemetry/core" "2.2.0" - "@opentelemetry/resources" "2.2.0" - "@opentelemetry/semantic-conventions" "^1.29.0" - -"@opentelemetry/semantic-conventions@^1.29.0": - version "1.37.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.37.0.tgz#aa2b4fa0b910b66a050c5ddfcac1d262e91a321a" - integrity sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA== - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@postman/form-data@~3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@postman/form-data/-/form-data-3.1.1.tgz#d0446d0d3639a291f5e800e89fa1d0d3723f9414" - integrity sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -"@postman/tough-cookie@4.1.3-postman.1", "@postman/tough-cookie@~4.1.3-postman.1": - version "4.1.3-postman.1" - resolved "https://registry.yarnpkg.com/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz#9b2aa98f0b42f7e8381ee7e66277cd0253a91f11" - integrity sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -"@postman/tunnel-agent@^0.6.3": - version "0.6.4" - resolved "https://registry.yarnpkg.com/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz#30ceaadf0bd21cc0677ad5d1abd238b0fe27ca38" - integrity sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ== - dependencies: - safe-buffer "^5.0.1" - -"@prettier/eslint@npm:prettier-eslint@^16.1.0": - version "16.4.2" - resolved "https://registry.yarnpkg.com/prettier-eslint/-/prettier-eslint-16.4.2.tgz#d84bff76e0ce4a6ffccacacb2474f7635ca8ac35" - integrity sha512-vtJAQEkaN8fW5QKl08t7A5KCjlZuDUNeIlr9hgolMS5s3+uzbfRHDwaRnzrdqnY2YpHDmeDS/8zY0MKQHXJtaA== - dependencies: - "@typescript-eslint/parser" "^6.21.0" - common-tags "^1.8.2" - dlv "^1.1.3" - eslint "^8.57.1" - indent-string "^4.0.0" - lodash.merge "^4.6.2" - loglevel-colored-level-prefix "^1.0.0" - prettier "^3.5.3" - pretty-format "^29.7.0" - require-relative "^0.8.7" - tslib "^2.8.1" - vue-eslint-parser "^9.4.3" - -"@prisma/prisma-fmt-wasm@^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085": - version "4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085" - resolved "https://registry.yarnpkg.com/@prisma/prisma-fmt-wasm/-/prisma-fmt-wasm-4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085.tgz#030f8a4448892c345b3c5c0558ca0ebf4642f3de" - integrity sha512-zYz3rFwPB82mVlHGknAPdnSY/a308dhPOblxQLcZgZTDRtDXOE1MgxoRAys+jekwR4/bm3+rZDPs1xsFMsPZig== - -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== - -"@rtsao/scc@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" - integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== - -"@s3pweb/keycloak-admin-client-cjs@26.2.4": - version "26.2.4" - resolved "https://registry.yarnpkg.com/@s3pweb/keycloak-admin-client-cjs/-/keycloak-admin-client-cjs-26.2.4.tgz#6a5a0c272cac17767abda167bc83a4ebf7c73b77" - integrity sha512-NbYC6HlAZuUIVI8L6xYDrpSzzCppqGwddPUY+L0ePYNY2rlPZKHWmnyvFYAPvQRXodgSxD/wLpio3/2acP8Ekw== - dependencies: - "@keycloak/keycloak-admin-client" "26.2.4" - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@sinonjs/commons@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== - dependencies: - "@sinonjs/commons" "^3.0.0" - -"@smithy/abort-controller@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.2.3.tgz#4615da3012b580ac3d1f0ee7b57ed7d7880bb29b" - integrity sha512-xWL9Mf8b7tIFuAlpjKtRPnHrR8XVrwTj5NPYO/QwZPtc0SDLsPxb56V5tzi5yspSMytISHybifez+4jlrx0vkQ== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/abort-controller@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.2.5.tgz#3386e8fff5a8d05930996d891d06803f2b7e5e2c" - integrity sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/chunked-blob-reader-native@^4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz#380266951d746b522b4ab2b16bfea6b451147b41" - integrity sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ== - dependencies: - "@smithy/util-base64" "^4.3.0" - tslib "^2.6.2" - -"@smithy/chunked-blob-reader@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz#776fec5eaa5ab5fa70d0d0174b7402420b24559c" - integrity sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA== - dependencies: - tslib "^2.6.2" - -"@smithy/config-resolver@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.4.0.tgz#9a33b7dd9b7e0475802acef53f41555257e104cd" - integrity sha512-Kkmz3Mup2PGp/HNJxhCWkLNdlajJORLSjwkcfrj0E7nu6STAEdcMR1ir5P9/xOmncx8xXfru0fbUYLlZog/cFg== - dependencies: - "@smithy/node-config-provider" "^4.3.3" - "@smithy/types" "^4.8.0" - "@smithy/util-config-provider" "^4.2.0" - "@smithy/util-endpoints" "^3.2.3" - "@smithy/util-middleware" "^4.2.3" - tslib "^2.6.2" - -"@smithy/config-resolver@^4.4.2", "@smithy/config-resolver@^4.4.3": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.4.3.tgz#37b0e3cba827272e92612e998a2b17e841e20bab" - integrity sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw== - dependencies: - "@smithy/node-config-provider" "^4.3.5" - "@smithy/types" "^4.9.0" - "@smithy/util-config-provider" "^4.2.0" - "@smithy/util-endpoints" "^3.2.5" - "@smithy/util-middleware" "^4.2.5" - tslib "^2.6.2" - -"@smithy/core@^3.17.1": - version "3.17.1" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.17.1.tgz#644aa4046b31c82d2c17276bcef2c6b78245dfeb" - integrity sha512-V4Qc2CIb5McABYfaGiIYLTmo/vwNIK7WXI5aGveBd9UcdhbOMwcvIMxIw/DJj1S9QgOMa/7FBkarMdIC0EOTEQ== - dependencies: - "@smithy/middleware-serde" "^4.2.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-stream" "^4.5.4" - "@smithy/util-utf8" "^4.2.0" - "@smithy/uuid" "^1.1.0" - tslib "^2.6.2" - -"@smithy/core@^3.17.2", "@smithy/core@^3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.18.0.tgz#6b58772b9421e17194f15f9c401147f4c39dc8ba" - integrity sha512-vGSDXOJFZgOPTatSI1ly7Gwyy/d/R9zh2TO3y0JZ0uut5qQ88p9IaWaZYIWSSqtdekNM4CGok/JppxbAff4KcQ== - dependencies: - "@smithy/middleware-serde" "^4.2.5" - "@smithy/protocol-http" "^5.3.5" - "@smithy/types" "^4.9.0" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-body-length-browser" "^4.2.0" - "@smithy/util-middleware" "^4.2.5" - "@smithy/util-stream" "^4.5.6" - "@smithy/util-utf8" "^4.2.0" - "@smithy/uuid" "^1.1.0" - tslib "^2.6.2" - -"@smithy/credential-provider-imds@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.3.tgz#b35d0d1f1b28f415e06282999eba2d53eb10a1c5" - integrity sha512-hA1MQ/WAHly4SYltJKitEsIDVsNmXcQfYBRv2e+q04fnqtAX5qXaybxy/fhUeAMCnQIdAjaGDb04fMHQefWRhw== - dependencies: - "@smithy/node-config-provider" "^4.3.3" - "@smithy/property-provider" "^4.2.3" - "@smithy/types" "^4.8.0" - "@smithy/url-parser" "^4.2.3" - tslib "^2.6.2" - -"@smithy/credential-provider-imds@^4.2.4", "@smithy/credential-provider-imds@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz#5acbcd1d02ae31700c2f027090c202d7315d70d3" - integrity sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ== - dependencies: - "@smithy/node-config-provider" "^4.3.5" - "@smithy/property-provider" "^4.2.5" - "@smithy/types" "^4.9.0" - "@smithy/url-parser" "^4.2.5" - tslib "^2.6.2" - -"@smithy/eventstream-codec@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.2.3.tgz#dd65d9050c322f0805ba62749a3801985a2f5394" - integrity sha512-rcr0VH0uNoMrtgKuY7sMfyKqbHc4GQaQ6Yp4vwgm+Z6psPuOgL+i/Eo/QWdXRmMinL3EgFM0Z1vkfyPyfzLmjw== - dependencies: - "@aws-crypto/crc32" "5.2.0" - "@smithy/types" "^4.8.0" - "@smithy/util-hex-encoding" "^4.2.0" - tslib "^2.6.2" - -"@smithy/eventstream-codec@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.2.5.tgz#331b3f23528137cb5f4ad861de7f34ddff68c62b" - integrity sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA== - dependencies: - "@aws-crypto/crc32" "5.2.0" - "@smithy/types" "^4.9.0" - "@smithy/util-hex-encoding" "^4.2.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-browser@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.3.tgz#57fb9c10daac12647a0b97ef04330d706cbe9494" - integrity sha512-EcS0kydOr2qJ3vV45y7nWnTlrPmVIMbUFOZbMG80+e2+xePQISX9DrcbRpVRFTS5Nqz3FiEbDcTCAV0or7bqdw== - dependencies: - "@smithy/eventstream-serde-universal" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-browser@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.5.tgz#54a680006539601ce71306d8bf2946e3462a47b3" - integrity sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw== - dependencies: - "@smithy/eventstream-serde-universal" "^4.2.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-config-resolver@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.3.tgz#ca1a7d272ae939aee303da40aa476656d785f75f" - integrity sha512-GewKGZ6lIJ9APjHFqR2cUW+Efp98xLu1KmN0jOWxQ1TN/gx3HTUPVbLciFD8CfScBj2IiKifqh9vYFRRXrYqXA== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-config-resolver@^4.3.4": - version "4.3.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.5.tgz#d1490aa127f43ac242495fa6e2e5833e1949a481" - integrity sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-node@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.3.tgz#f1b33bb576bf7222b6bd6bc2ad845068ccf53f16" - integrity sha512-uQobOTQq2FapuSOlmGLUeGTpvcBLE5Fc7XjERUSk4dxEi4AhTwuyHYZNAvL4EMUp7lzxxkKDFaJ1GY0ovrj0Kg== - dependencies: - "@smithy/eventstream-serde-universal" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-node@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.5.tgz#7dd64e0ba64fa930959f3d5b7995c310573ecaf3" - integrity sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg== - dependencies: - "@smithy/eventstream-serde-universal" "^4.2.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-universal@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.3.tgz#86194daa2cd2496e413723465360d80f32ad7252" - integrity sha512-QIvH/CKOk1BZPz/iwfgbh1SQD5Y0lpaw2kLA8zpLRRtYMPXeYUEWh+moTaJyqDaKlbrB174kB7FSRFiZ735tWw== - dependencies: - "@smithy/eventstream-codec" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/eventstream-serde-universal@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.5.tgz#34189de45cf5e1d9cb59978e94b76cc210fa984f" - integrity sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q== - dependencies: - "@smithy/eventstream-codec" "^4.2.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/fetch-http-handler@^5.3.4": - version "5.3.4" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.4.tgz#af6dd2f63550494c84ef029a5ceda81ef46965d3" - integrity sha512-bwigPylvivpRLCm+YK9I5wRIYjFESSVwl8JQ1vVx/XhCw0PtCi558NwTnT2DaVCl5pYlImGuQTSwMsZ+pIavRw== - dependencies: - "@smithy/protocol-http" "^5.3.3" - "@smithy/querystring-builder" "^4.2.3" - "@smithy/types" "^4.8.0" - "@smithy/util-base64" "^4.3.0" - tslib "^2.6.2" - -"@smithy/fetch-http-handler@^5.3.5", "@smithy/fetch-http-handler@^5.3.6": - version "5.3.6" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz#d9dcb8d8ca152918224492f4d1cc1b50df93ae13" - integrity sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg== - dependencies: - "@smithy/protocol-http" "^5.3.5" - "@smithy/querystring-builder" "^4.2.5" - "@smithy/types" "^4.9.0" - "@smithy/util-base64" "^4.3.0" - tslib "^2.6.2" - -"@smithy/hash-blob-browser@^4.2.4": - version "4.2.4" - resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.4.tgz#c7226d2ba2a394acf6e90510d08f7c3003f516d1" - integrity sha512-W7eIxD+rTNsLB/2ynjmbdeP7TgxRXprfvqQxKFEfy9HW2HeD7t+g+KCIrY0pIn/GFjA6/fIpH+JQnfg5TTk76Q== - dependencies: - "@smithy/chunked-blob-reader" "^5.2.0" - "@smithy/chunked-blob-reader-native" "^4.2.1" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/hash-blob-browser@^4.2.5": - version "4.2.6" - resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.6.tgz#53d5ae0a069ae4a93abbc7165efe341dca0f9489" - integrity sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw== - dependencies: - "@smithy/chunked-blob-reader" "^5.2.0" - "@smithy/chunked-blob-reader-native" "^4.2.1" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/hash-node@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.3.tgz#c85711fca84e022f05c71b921f98cb6a0f48e5ca" - integrity sha512-6+NOdZDbfuU6s1ISp3UOk5Rg953RJ2aBLNLLBEcamLjHAg1Po9Ha7QIB5ZWhdRUVuOUrT8BVFR+O2KIPmw027g== - dependencies: - "@smithy/types" "^4.8.0" - "@smithy/util-buffer-from" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/hash-node@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.5.tgz#fb751ec4a4c6347612458430f201f878adc787f6" - integrity sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA== - dependencies: - "@smithy/types" "^4.9.0" - "@smithy/util-buffer-from" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/hash-stream-node@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-4.2.3.tgz#8ddae1f5366513cbbec3acb6f54e3ec1b332db88" - integrity sha512-EXMSa2yiStVII3x/+BIynyOAZlS7dGvI7RFrzXa/XssBgck/7TXJIvnjnCu328GY/VwHDC4VeDyP1S4rqwpYag== - dependencies: - "@smithy/types" "^4.8.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/hash-stream-node@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-4.2.5.tgz#f200e6b755cb28f03968c199231774c3ad33db28" - integrity sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q== - dependencies: - "@smithy/types" "^4.9.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/invalid-dependency@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.3.tgz#4f126ddde90fe3d69d522fc37256ee853246c1ec" - integrity sha512-Cc9W5DwDuebXEDMpOpl4iERo8I0KFjTnomK2RMdhhR87GwrSmUmwMxS4P5JdRf+LsjOdIqumcerwRgYMr/tZ9Q== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/invalid-dependency@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz#58d997e91e7683ffc59882d8fcb180ed9aa9c7dd" - integrity sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/is-array-buffer@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" - integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== - dependencies: - tslib "^2.6.2" - -"@smithy/is-array-buffer@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz#b0f874c43887d3ad44f472a0f3f961bcce0550c2" - integrity sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ== - dependencies: - tslib "^2.6.2" - -"@smithy/md5-js@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-4.2.3.tgz#a89c324ff61c64c25b4895fa16d9358f7e3cc746" - integrity sha512-5+4bUEJQi/NRgzdA5SVXvAwyvEnD0ZAiKzV3yLO6dN5BG8ScKBweZ8mxXXUtdxq+Dx5k6EshKk0XJ7vgvIPSnA== - dependencies: - "@smithy/types" "^4.8.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/md5-js@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-4.2.5.tgz#ca16f138dd0c4e91a61d3df57e8d4d15d1ddc97e" - integrity sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg== - dependencies: - "@smithy/types" "^4.9.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/middleware-content-length@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.3.tgz#b7d1d79ae674dad17e35e3518db4b1f0adc08964" - integrity sha512-/atXLsT88GwKtfp5Jr0Ks1CSa4+lB+IgRnkNrrYP0h1wL4swHNb0YONEvTceNKNdZGJsye+W2HH8W7olbcPUeA== - dependencies: - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/middleware-content-length@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz#a6942ce2d7513b46f863348c6c6a8177e9ace752" - integrity sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A== - dependencies: - "@smithy/protocol-http" "^5.3.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/middleware-endpoint@^4.3.5": - version "4.3.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.5.tgz#c22f82f83f0b5cc6c0866a2a87b65bc2e79af352" - integrity sha512-SIzKVTvEudFWJbxAaq7f2GvP3jh2FHDpIFI6/VAf4FOWGFZy0vnYMPSRj8PGYI8Hjt29mvmwSRgKuO3bK4ixDw== - dependencies: - "@smithy/core" "^3.17.1" - "@smithy/middleware-serde" "^4.2.3" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - "@smithy/url-parser" "^4.2.3" - "@smithy/util-middleware" "^4.2.3" - tslib "^2.6.2" - -"@smithy/middleware-endpoint@^4.3.6", "@smithy/middleware-endpoint@^4.3.7": - version "4.3.7" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.7.tgz#ef7054cf969484caf173e6013fb0339753ca02bc" - integrity sha512-i8Mi8OuY6Yi82Foe3iu7/yhBj1HBRoOQwBSsUNYglJTNSFaWYTNM2NauBBs/7pq2sqkLRqeUXA3Ogi2utzpUlQ== - dependencies: - "@smithy/core" "^3.18.0" - "@smithy/middleware-serde" "^4.2.5" - "@smithy/node-config-provider" "^4.3.5" - "@smithy/shared-ini-file-loader" "^4.4.0" - "@smithy/types" "^4.9.0" - "@smithy/url-parser" "^4.2.5" - "@smithy/util-middleware" "^4.2.5" - tslib "^2.6.2" - -"@smithy/middleware-retry@^4.4.5": - version "4.4.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.5.tgz#5bdb6ba1be6a97272b79fdac99db40c5e7ab81e0" - integrity sha512-DCaXbQqcZ4tONMvvdz+zccDE21sLcbwWoNqzPLFlZaxt1lDtOE2tlVpRSwcTOJrjJSUThdgEYn7HrX5oLGlK9A== - dependencies: - "@smithy/node-config-provider" "^4.3.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/service-error-classification" "^4.2.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-retry" "^4.2.3" - "@smithy/uuid" "^1.1.0" - tslib "^2.6.2" - -"@smithy/middleware-retry@^4.4.6": - version "4.4.7" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.7.tgz#adcffe9585e7dc3fc279418ededddc1391ebc5b7" - integrity sha512-E7Vc6WHCHlzDRTx1W0jZ6J1L6ziEV0PIWcUdmfL4y+c8r7WYr6I+LkQudaD8Nfb7C5c4P3SQ972OmXHtv6m/OA== - dependencies: - "@smithy/node-config-provider" "^4.3.5" - "@smithy/protocol-http" "^5.3.5" - "@smithy/service-error-classification" "^4.2.5" - "@smithy/smithy-client" "^4.9.3" - "@smithy/types" "^4.9.0" - "@smithy/util-middleware" "^4.2.5" - "@smithy/util-retry" "^4.2.5" - "@smithy/uuid" "^1.1.0" - tslib "^2.6.2" - -"@smithy/middleware-serde@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.3.tgz#a827e9c4ea9e51c79cca4d6741d582026a8b53eb" - integrity sha512-8g4NuUINpYccxiCXM5s1/V+uLtts8NcX4+sPEbvYQDZk4XoJfDpq5y2FQxfmUL89syoldpzNzA0R9nhzdtdKnQ== - dependencies: - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/middleware-serde@^4.2.4", "@smithy/middleware-serde@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.5.tgz#b8848043d2965ef3fc2ad895c877fa1f42a9cd86" - integrity sha512-La1ldWTJTZ5NqQyPqnCNeH9B+zjFhrNoQIL1jTh4zuqXRlmXhxYHhMtI1/92OlnoAtp6JoN7kzuwhWoXrBwPqg== - dependencies: - "@smithy/protocol-http" "^5.3.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/middleware-stack@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.3.tgz#5a315aa9d0fd4faaa248780297c8cbacc31c2eba" - integrity sha512-iGuOJkH71faPNgOj/gWuEGS6xvQashpLwWB1HjHq1lNNiVfbiJLpZVbhddPuDbx9l4Cgl0vPLq5ltRfSaHfspA== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/middleware-stack@^4.2.4", "@smithy/middleware-stack@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz#2d13415ed3561c882594c8e6340b801d9a2eb222" - integrity sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/node-config-provider@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.3.tgz#44140a1e6bc666bcf16faf68c35d3dae4ba8cad5" - integrity sha512-NzI1eBpBSViOav8NVy1fqOlSfkLgkUjUTlohUSgAEhHaFWA3XJiLditvavIP7OpvTjDp5u2LhtlBhkBlEisMwA== - dependencies: - "@smithy/property-provider" "^4.2.3" - "@smithy/shared-ini-file-loader" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/node-config-provider@^4.3.4", "@smithy/node-config-provider@^4.3.5": - version "4.3.5" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz#c09137a79c2930dcc30e6c8bb4f2608d72c1e2c9" - integrity sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg== - dependencies: - "@smithy/property-provider" "^4.2.5" - "@smithy/shared-ini-file-loader" "^4.4.0" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/node-http-handler@^4.4.3": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.3.tgz#fb2d16719cb4e8df0c189e8bde60e837df5c0c5b" - integrity sha512-MAwltrDB0lZB/H6/2M5PIsISSwdI5yIh6DaBB9r0Flo9nx3y0dzl/qTMJPd7tJvPdsx6Ks/cwVzheGNYzXyNbQ== - dependencies: - "@smithy/abort-controller" "^4.2.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/querystring-builder" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/node-http-handler@^4.4.4", "@smithy/node-http-handler@^4.4.5": - version "4.4.5" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz#2aea598fdf3dc4e32667d673d48abd4a073665f4" - integrity sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw== - dependencies: - "@smithy/abort-controller" "^4.2.5" - "@smithy/protocol-http" "^5.3.5" - "@smithy/querystring-builder" "^4.2.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/property-provider@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.3.tgz#a6c82ca0aa1c57f697464bee496f3fec58660864" - integrity sha512-+1EZ+Y+njiefCohjlhyOcy1UNYjT+1PwGFHCxA/gYctjg3DQWAU19WigOXAco/Ql8hZokNehpzLd0/+3uCreqQ== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/property-provider@^4.2.4", "@smithy/property-provider@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.5.tgz#f75dc5735d29ca684abbc77504be9246340a43f0" - integrity sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/protocol-http@^5.3.3": - version "5.3.3" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.3.tgz#55b35c18bdc0f6d86e78f63961e50ba4ff1c5d73" - integrity sha512-Mn7f/1aN2/jecywDcRDvWWWJF4uwg/A0XjFMJtj72DsgHTByfjRltSqcT9NyE9RTdBSN6X1RSXrhn/YWQl8xlw== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/protocol-http@^5.3.4", "@smithy/protocol-http@^5.3.5": - version "5.3.5" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.5.tgz#a8f4296dd6d190752589e39ee95298d5c65a60db" - integrity sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/querystring-builder@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.3.tgz#ca273ae8c21fce01a52632202679c0f9e2acf41a" - integrity sha512-LOVCGCmwMahYUM/P0YnU/AlDQFjcu+gWbFJooC417QRB/lDJlWSn8qmPSDp+s4YVAHOgtgbNG4sR+SxF/VOcJQ== - dependencies: - "@smithy/types" "^4.8.0" - "@smithy/util-uri-escape" "^4.2.0" - tslib "^2.6.2" - -"@smithy/querystring-builder@^4.2.4", "@smithy/querystring-builder@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz#00cafa5a4055600ab8058e26db42f580146b91f3" - integrity sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg== - dependencies: - "@smithy/types" "^4.9.0" - "@smithy/util-uri-escape" "^4.2.0" - tslib "^2.6.2" - -"@smithy/querystring-parser@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.3.tgz#b6d7d5cd300b4083c62d9bd30915f782d01f503e" - integrity sha512-cYlSNHcTAX/wc1rpblli3aUlLMGgKZ/Oqn8hhjFASXMCXjIqeuQBei0cnq2JR8t4RtU9FpG6uyl6PxyArTiwKA== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/querystring-parser@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz#61d2e77c62f44196590fa0927dbacfbeaffe8c53" - integrity sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/service-error-classification@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.3.tgz#ecb41dd514841eebb93e91012ae5e343040f6828" - integrity sha512-NkxsAxFWwsPsQiwFG2MzJ/T7uIR6AQNh1SzcxSUnmmIqIQMlLRQDKhc17M7IYjiuBXhrQRjQTo3CxX+DobS93g== - dependencies: - "@smithy/types" "^4.8.0" - -"@smithy/service-error-classification@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz#a64eb78e096e59cc71141e3fea2b4194ce59b4fd" - integrity sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ== - dependencies: - "@smithy/types" "^4.9.0" - -"@smithy/shared-ini-file-loader@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.3.tgz#1d5162cd3a14f57e4fde56f65aa188e8138c1248" - integrity sha512-9f9Ixej0hFhroOK2TxZfUUDR13WVa8tQzhSzPDgXe5jGL3KmaM9s8XN7RQwqtEypI82q9KHnKS71CJ+q/1xLtQ== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/shared-ini-file-loader@^4.3.4", "@smithy/shared-ini-file-loader@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz#a2f8282f49982f00bafb1fa8cb7fc188a202a594" - integrity sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/signature-v4@^5.3.3": - version "5.3.3" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.3.tgz#5ff13cfaa29cb531061c2582cb599b39e040e52e" - integrity sha512-CmSlUy+eEYbIEYN5N3vvQTRfqt0lJlQkaQUIf+oizu7BbDut0pozfDjBGecfcfWf7c62Yis4JIEgqQ/TCfodaA== - dependencies: - "@smithy/is-array-buffer" "^4.2.0" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - "@smithy/util-hex-encoding" "^4.2.0" - "@smithy/util-middleware" "^4.2.3" - "@smithy/util-uri-escape" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/signature-v4@^5.3.4": - version "5.3.5" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.5.tgz#13ab710653f9f16c325ee7e0a102a44f73f2643f" - integrity sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w== - dependencies: - "@smithy/is-array-buffer" "^4.2.0" - "@smithy/protocol-http" "^5.3.5" - "@smithy/types" "^4.9.0" - "@smithy/util-hex-encoding" "^4.2.0" - "@smithy/util-middleware" "^4.2.5" - "@smithy/util-uri-escape" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/smithy-client@^4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.9.1.tgz#a36e456e837121b2ded6f7d5f1f30b205c446e20" - integrity sha512-Ngb95ryR5A9xqvQFT5mAmYkCwbXvoLavLFwmi7zVg/IowFPCfiqRfkOKnbc/ZRL8ZKJ4f+Tp6kSu6wjDQb8L/g== - dependencies: - "@smithy/core" "^3.17.1" - "@smithy/middleware-endpoint" "^4.3.5" - "@smithy/middleware-stack" "^4.2.3" - "@smithy/protocol-http" "^5.3.3" - "@smithy/types" "^4.8.0" - "@smithy/util-stream" "^4.5.4" - tslib "^2.6.2" - -"@smithy/smithy-client@^4.9.2", "@smithy/smithy-client@^4.9.3": - version "4.9.3" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.9.3.tgz#1c347fb7ec2e7fd3d61b84d8affe97c87e4bca5d" - integrity sha512-8tlueuTgV5n7inQCkhyptrB3jo2AO80uGrps/XTYZivv5MFQKKBj3CIWIGMI2fRY5LEduIiazOhAWdFknY1O9w== - dependencies: - "@smithy/core" "^3.18.0" - "@smithy/middleware-endpoint" "^4.3.7" - "@smithy/middleware-stack" "^4.2.5" - "@smithy/protocol-http" "^5.3.5" - "@smithy/types" "^4.9.0" - "@smithy/util-stream" "^4.5.6" - tslib "^2.6.2" - -"@smithy/types@^4.8.0": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.8.0.tgz#e6f65e712478910b74747081e6046e68159f767d" - integrity sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ== - dependencies: - tslib "^2.6.2" - -"@smithy/types@^4.8.1", "@smithy/types@^4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.9.0.tgz#c6636ddfa142e1ddcb6e4cf5f3e1a628d420486f" - integrity sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA== - dependencies: - tslib "^2.6.2" - -"@smithy/url-parser@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.3.tgz#82508f273a3f074d47d0919f7ce08028c6575c2f" - integrity sha512-I066AigYvY3d9VlU3zG9XzZg1yT10aNqvCaBTw9EPgu5GrsEl1aUkcMvhkIXascYH1A8W0LQo3B1Kr1cJNcQEw== - dependencies: - "@smithy/querystring-parser" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/url-parser@^4.2.4", "@smithy/url-parser@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.5.tgz#2fea006108f17f7761432c7ef98d6aa003421487" - integrity sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ== - dependencies: - "@smithy/querystring-parser" "^4.2.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/util-base64@^4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.3.0.tgz#5e287b528793aa7363877c1a02cd880d2e76241d" - integrity sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ== - dependencies: - "@smithy/util-buffer-from" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/util-body-length-browser@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz#04e9fc51ee7a3e7f648a4b4bcdf96c350cfa4d61" - integrity sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg== - dependencies: - tslib "^2.6.2" - -"@smithy/util-body-length-node@^4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz#79c8a5d18e010cce6c42d5cbaf6c1958523e6fec" - integrity sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA== - dependencies: - tslib "^2.6.2" - -"@smithy/util-buffer-from@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" - integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== - dependencies: - "@smithy/is-array-buffer" "^2.2.0" - tslib "^2.6.2" - -"@smithy/util-buffer-from@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz#7abd12c4991b546e7cee24d1e8b4bfaa35c68a9d" - integrity sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew== - dependencies: - "@smithy/is-array-buffer" "^4.2.0" - tslib "^2.6.2" - -"@smithy/util-config-provider@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz#2e4722937f8feda4dcb09672c59925a4e6286cfc" - integrity sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q== - dependencies: - tslib "^2.6.2" - -"@smithy/util-defaults-mode-browser@^4.3.4": - version "4.3.4" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.4.tgz#ed96651c32ac0de55b066fcb07a296837373212f" - integrity sha512-qI5PJSW52rnutos8Bln8nwQZRpyoSRN6k2ajyoUHNMUzmWqHnOJCnDELJuV6m5PML0VkHI+XcXzdB+6awiqYUw== - dependencies: - "@smithy/property-provider" "^4.2.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/util-defaults-mode-browser@^4.3.5": - version "4.3.6" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.6.tgz#e6625f55a73c9897648496baee7c6c00d2bc96ce" - integrity sha512-kbpuXbEf2YQ9zEE6eeVnUCQWO0e1BjMnKrXL8rfXgiWA0m8/E0leU4oSNzxP04WfCmW8vjEqaDeXWxwE4tpOjQ== - dependencies: - "@smithy/property-provider" "^4.2.5" - "@smithy/smithy-client" "^4.9.3" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/util-defaults-mode-node@^4.2.6": - version "4.2.6" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.6.tgz#01b7ff4605f6f981972083fee22d036e5dc4be38" - integrity sha512-c6M/ceBTm31YdcFpgfgQAJaw3KbaLuRKnAz91iMWFLSrgxRpYm03c3bu5cpYojNMfkV9arCUelelKA7XQT36SQ== - dependencies: - "@smithy/config-resolver" "^4.4.0" - "@smithy/credential-provider-imds" "^4.2.3" - "@smithy/node-config-provider" "^4.3.3" - "@smithy/property-provider" "^4.2.3" - "@smithy/smithy-client" "^4.9.1" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/util-defaults-mode-node@^4.2.8": - version "4.2.9" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.9.tgz#b500d660ca8c6d665d068b161e28f8718052a065" - integrity sha512-dgyribrVWN5qE5usYJ0m5M93mVM3L3TyBPZWe1Xl6uZlH2gzfQx3dz+ZCdW93lWqdedJRkOecnvbnoEEXRZ5VQ== - dependencies: - "@smithy/config-resolver" "^4.4.3" - "@smithy/credential-provider-imds" "^4.2.5" - "@smithy/node-config-provider" "^4.3.5" - "@smithy/property-provider" "^4.2.5" - "@smithy/smithy-client" "^4.9.3" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/util-endpoints@^3.2.3": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.3.tgz#8bbb80f1ad5769d9f73992c5979eea3b74d7baa9" - integrity sha512-aCfxUOVv0CzBIkU10TubdgKSx5uRvzH064kaiPEWfNIvKOtNpu642P4FP1hgOFkjQIkDObrfIDnKMKkeyrejvQ== - dependencies: - "@smithy/node-config-provider" "^4.3.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/util-endpoints@^3.2.4", "@smithy/util-endpoints@^3.2.5": - version "3.2.5" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz#9e0fc34e38ddfbbc434d23a38367638dc100cb14" - integrity sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A== - dependencies: - "@smithy/node-config-provider" "^4.3.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/util-hex-encoding@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz#1c22ea3d1e2c3a81ff81c0a4f9c056a175068a7b" - integrity sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw== - dependencies: - tslib "^2.6.2" - -"@smithy/util-middleware@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.2.3.tgz#7c73416a6e3d3207a2d34a1eadd9f2b6a9811bd6" - integrity sha512-v5ObKlSe8PWUHCqEiX2fy1gNv6goiw6E5I/PN2aXg3Fb/hse0xeaAnSpXDiWl7x6LamVKq7senB+m5LOYHUAHw== - dependencies: - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/util-middleware@^4.2.4", "@smithy/util-middleware@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.2.5.tgz#1ace865afe678fd4b0f9217197e2fe30178d4835" - integrity sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA== - dependencies: - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/util-retry@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.3.tgz#b1e5c96d96aaf971b68323ff8ba8754f914f22a0" - integrity sha512-lLPWnakjC0q9z+OtiXk+9RPQiYPNAovt2IXD3CP4LkOnd9NpUsxOjMx1SnoUVB7Orb7fZp67cQMtTBKMFDvOGg== - dependencies: - "@smithy/service-error-classification" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/util-retry@^4.2.4", "@smithy/util-retry@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.5.tgz#70fe4fbbfb9ad43a9ce2ba4ed111ff7b30d7b333" - integrity sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg== - dependencies: - "@smithy/service-error-classification" "^4.2.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/util-stream@^4.5.4": - version "4.5.4" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.4.tgz#bfc60e2714c2065b8e7e91ca921cc31c73efdbd4" - integrity sha512-+qDxSkiErejw1BAIXUFBSfM5xh3arbz1MmxlbMCKanDDZtVEQ7PSKW9FQS0Vud1eI/kYn0oCTVKyNzRlq+9MUw== - dependencies: - "@smithy/fetch-http-handler" "^5.3.4" - "@smithy/node-http-handler" "^4.4.3" - "@smithy/types" "^4.8.0" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-buffer-from" "^4.2.0" - "@smithy/util-hex-encoding" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/util-stream@^4.5.5", "@smithy/util-stream@^4.5.6": - version "4.5.6" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.6.tgz#ebee9e52adeb6f88337778b2f3356a2cc615298c" - integrity sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ== - dependencies: - "@smithy/fetch-http-handler" "^5.3.6" - "@smithy/node-http-handler" "^4.4.5" - "@smithy/types" "^4.9.0" - "@smithy/util-base64" "^4.3.0" - "@smithy/util-buffer-from" "^4.2.0" - "@smithy/util-hex-encoding" "^4.2.0" - "@smithy/util-utf8" "^4.2.0" - tslib "^2.6.2" - -"@smithy/util-uri-escape@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz#096a4cec537d108ac24a68a9c60bee73fc7e3a9e" - integrity sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA== - dependencies: - tslib "^2.6.2" - -"@smithy/util-utf8@^2.0.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" - integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== - dependencies: - "@smithy/util-buffer-from" "^2.2.0" - tslib "^2.6.2" - -"@smithy/util-utf8@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.2.0.tgz#8b19d1514f621c44a3a68151f3d43e51087fed9d" - integrity sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw== - dependencies: - "@smithy/util-buffer-from" "^4.2.0" - tslib "^2.6.2" - -"@smithy/util-waiter@^4.2.3": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.2.3.tgz#4c662009db101bc60aed07815d359e90904caef2" - integrity sha512-5+nU///E5sAdD7t3hs4uwvCTWQtTR8JwKwOCSJtBRx0bY1isDo1QwH87vRK86vlFLBTISqoDA2V6xvP6nF1isQ== - dependencies: - "@smithy/abort-controller" "^4.2.3" - "@smithy/types" "^4.8.0" - tslib "^2.6.2" - -"@smithy/util-waiter@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.2.5.tgz#e527816edae20ec5f68b25685f4b21d93424ea86" - integrity sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g== - dependencies: - "@smithy/abort-controller" "^4.2.5" - "@smithy/types" "^4.9.0" - tslib "^2.6.2" - -"@smithy/uuid@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@smithy/uuid/-/uuid-1.1.0.tgz#9fd09d3f91375eab94f478858123387df1cda987" - integrity sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw== - dependencies: - tslib "^2.6.2" - -"@so-ric/colorspace@^1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@so-ric/colorspace/-/colorspace-1.1.6.tgz#62515d8b9f27746b76950a83bde1af812d91923b" - integrity sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw== - dependencies: - color "^5.0.2" - text-hex "1.0.x" - -"@supercharge/request-ip@^1.1.2": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@supercharge/request-ip/-/request-ip-1.2.0.tgz#b8a8164322e09de3fa9b6f556885795c4841a7d4" - integrity sha512-wlt6JW69MHqLY2M6Sm/jVyCojNRKq2CBvwH0Hbx24SFhDQQGkgEjeKxVutDxHSyrWixFaOSLXC27euzxijhyMQ== - -"@testim/chrome-version@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.4.tgz#86e04e677cd6c05fa230dd15ac223fa72d1d7090" - integrity sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g== - -"@theguild/federation-composition@^0.20.1": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@theguild/federation-composition/-/federation-composition-0.20.2.tgz#d6b449c6cf4d3f09426fcdd9bb20f2cb83742a6b" - integrity sha512-QI4iSdrc4JvCWnMb1QbiHnEpdD33KGdiU66qfWOcM8ENebRGHkGjXDnUrVJ8F9g1dmCRMTNfn2NFGqTcDpeYXw== - dependencies: - constant-case "^3.0.4" - debug "4.4.3" - json5 "^2.2.3" - lodash.sortby "^4.7.0" - -"@tootallnate/quickjs-emscripten@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" - integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== - -"@types/accepts@^1.3.5": - version "1.3.7" - resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265" - integrity sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ== - dependencies: - "@types/node" "*" - -"@types/amqp-connection-manager@^2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@types/amqp-connection-manager/-/amqp-connection-manager-2.0.12.tgz#809a01207193ffc15b1689806b51dfeba7b3f9f3" - integrity sha512-2GX1jG6ECpEXQF0X68gTTZc8MQ8GA0dM2mAd1irTpWlKzGKlGzCBtb1YnqLHozNNsoLtGI6UXSp0q06jU1LA6g== - dependencies: - "@types/amqplib" "*" - -"@types/amqplib@*": - version "0.10.8" - resolved "https://registry.yarnpkg.com/@types/amqplib/-/amqplib-0.10.8.tgz#23f2945d055e9fd583da672aa5ec0c7350b9e4e6" - integrity sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA== - dependencies: - "@types/node" "*" - -"@types/babel__core@^7.1.14": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" - integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" - integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== - dependencies: - "@babel/types" "^7.28.2" - -"@types/body-parser@*": - version "1.19.6" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" - integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/body-parser@1.19.2": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/busboy@^1.5.0": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/busboy/-/busboy-1.5.4.tgz#0038c31102ca90f2a7f0d8bc27ee5ebf1088e230" - integrity sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw== - dependencies: - "@types/node" "*" - -"@types/connect@*": - version "3.4.38" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" - integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== - dependencies: - "@types/node" "*" - -"@types/cors@2.8.12": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== - -"@types/express-serve-static-core@4.17.31": - version "4.17.31" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" - integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.33": - version "4.19.7" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz#f1d306dcc03b1aafbfb6b4fe684cce8a31cffc10" - integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express@4.17.14": - version "4.17.14" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" - integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.18" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/express@^4.17.6": - version "4.17.25" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.25.tgz#070c8c73a6fee6936d65c195dbbfb7da5026649b" - integrity sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "^1" - -"@types/fs-extra@^11.0.4": - version "11.0.4" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" - integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== - dependencies: - "@types/jsonfile" "*" - "@types/node" "*" - -"@types/geojson@^7946.0.16": - version "7946.0.16" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a" - integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== - -"@types/graceful-fs@^4.1.3": - version "4.1.9" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" - integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== - dependencies: - "@types/node" "*" - -"@types/http-errors@*": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" - integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" - integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== - -"@types/istanbul-lib-report@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" - integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" - integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@^29.5.12": - version "29.5.14" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" - integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@types/jsonfile@*": - version "6.1.4" - resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" - integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== - dependencies: - "@types/node" "*" - -"@types/jsonwebtoken@^9.0.8": - version "9.0.10" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz#a7932a47177dcd4283b6146f3bd5c26d82647f09" - integrity sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA== - dependencies: - "@types/ms" "*" - "@types/node" "*" - -"@types/lodash-es@^4.17.12": - version "4.17.12" - resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" - integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.17.20" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93" - integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA== - -"@types/long@^4.0.0": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" - integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== - -"@types/luxon@~3.4.0": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" - integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== - -"@types/mime@^1": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" - integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== - -"@types/ms@*": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" - integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== - -"@types/node-cache@^4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@types/node-cache/-/node-cache-4.2.5.tgz#dac6be8bf8f486db4eef66f52f7bb9e90b65d2c1" - integrity sha512-faK2Owokboz53g8ooq2dw3iDJ6/HMTCIa2RvMte5WMTiABy+wA558K+iuyRtlR67Un5q9gEKysSDtqZYbSa0Pg== - dependencies: - node-cache "*" - -"@types/node@*", "@types/node@>=13.7.0", "@types/node@^24.0.13": - version "24.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.9.1.tgz#b7360b3c789089e57e192695a855aa4f6981a53c" - integrity sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg== - dependencies: - undici-types "~7.16.0" - -"@types/node@10.12.7": - version "10.12.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.7.tgz#195808b2d4b2e7c33e75e7d9b24aeee88f94660d" - integrity sha512-Zh5Z4kACfbeE8aAOYh9mqotRxaZMro8MbBQtR8vEXOMiZo2rGEh2LayJijKdlu48YnS6y2EFU/oo2NCe5P6jGw== - -"@types/node@^10.1.0": - version "10.17.60" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" - integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== - -"@types/object-path@^0.11.1": - version "0.11.4" - resolved "https://registry.yarnpkg.com/@types/object-path/-/object-path-0.11.4.tgz#a14529227500faf7891d003b2c7f5772c4a5dfdc" - integrity sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw== - -"@types/qs@*": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" - integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== - -"@types/ramda@types/npm-ramda#dist": - version "0.25.0" - resolved "https://codeload.github.com/types/npm-ramda/tar.gz/9529aa3c8ff70ff84afcbc0be83443c00f30ea90" - -"@types/range-parser@*": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" - integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== - -"@types/redis@^2.8.32": - version "2.8.32" - resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.32.tgz#1d3430219afbee10f8cfa389dad2571a05ecfb11" - integrity sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w== - dependencies: - "@types/node" "*" - -"@types/semver@^7.5.8": - version "7.7.1" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" - integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== - -"@types/send@*": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@types/send/-/send-1.2.1.tgz#6a784e45543c18c774c049bff6d3dbaf045c9c74" - integrity sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ== - dependencies: - "@types/node" "*" - -"@types/send@<1": - version "0.17.6" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.6.tgz#aeb5385be62ff58a52cd5459daa509ae91651d25" - integrity sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/serve-static@*": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-2.2.0.tgz#d4a447503ead0d1671132d1ab6bd58b805d8de6a" - integrity sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ== - dependencies: - "@types/http-errors" "*" - "@types/node" "*" - -"@types/serve-static@^1": - version "1.15.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.10.tgz#768169145a778f8f5dfcb6360aead414a3994fee" - integrity sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw== - dependencies: - "@types/http-errors" "*" - "@types/node" "*" - "@types/send" "<1" - -"@types/stack-utils@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" - integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== - -"@types/triple-beam@^1.3.2": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" - integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== - -"@types/ws@^8.18.0": - version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" - integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== - dependencies: - "@types/node" "*" - -"@types/yargs-parser@*": - version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" - integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== - -"@types/yargs@^17.0.8": - version "17.0.34" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.34.tgz#1c2f9635b71d5401827373a01ce2e8a7670ea839" - integrity sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A== - dependencies: - "@types/yargs-parser" "*" - -"@types/yarnpkg__lockfile@^1.1.9": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.9.tgz#b3c8e8d66dc8ce79827f422a660a557cda9ded14" - integrity sha512-GD4Fk15UoP5NLCNor51YdfL9MSdldKCqOC9EssrRw3HVfar9wUZ5y8Lfnp+qVD6hIinLr8ygklDYnmlnlQo12Q== - -"@types/yauzl@^2.9.1": - version "2.10.3" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" - integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== - dependencies: - "@types/node" "*" - -"@typescript-eslint/parser@^6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" - integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== - dependencies: - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" - integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== - dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - -"@typescript-eslint/types@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" - integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== - -"@typescript-eslint/typescript-estree@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" - integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== - dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - minimatch "9.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" - -"@typescript-eslint/visitor-keys@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" - integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== - dependencies: - "@typescript-eslint/types" "6.21.0" - eslint-visitor-keys "^3.4.1" - -"@tyriar/fibonacci-heap@^2.0.7": - version "2.0.9" - resolved "https://registry.yarnpkg.com/@tyriar/fibonacci-heap/-/fibonacci-heap-2.0.9.tgz#df3dcbdb1b9182168601f6318366157ee16666e9" - integrity sha512-bYuSNomfn4hu2tPiDN+JZtnzCpSpbJ/PNeulmocDy3xN2X5OkJL65zo6rPZp65cPPhLF9vfT/dgE+RtFRCSxOA== - -"@ungap/structured-clone@^1.2.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" - integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== - -"@whatwg-node/promise-helpers@^1.0.0": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz#3b54987ad6517ef6db5920c66a6f0dada606587d" - integrity sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA== - dependencies: - tslib "^2.6.3" - -"@wry/caches@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" - integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== - dependencies: - tslib "^2.3.0" - -"@wry/context@^0.7.0": - version "0.7.4" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" - integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== - dependencies: - tslib "^2.3.0" - -"@wry/equality@^0.5.6": - version "0.5.7" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" - integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== - dependencies: - tslib "^2.3.0" - -"@wry/trie@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" - integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== - dependencies: - tslib "^2.3.0" - -"@yarnpkg/lockfile@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" - integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== - -accepts@^1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-import-attributes@^1.9.5: - version "1.9.5" - resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" - integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^8.14.0, acorn@^8.9.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== - -agent-base@^7.1.0, agent-base@^7.1.2: - version "7.1.4" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" - integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== - -agentkeepalive@^3.4.1: - version "3.5.3" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.3.tgz#c210afce942b4287e2df2fbfe6c0d57eda2ce634" - integrity sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw== - dependencies: - humanize-ms "^1.2.1" - -ajv@^6.12.3, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - 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" - -amqp-connection-manager@^4.1.14: - version "4.1.15" - resolved "https://registry.yarnpkg.com/amqp-connection-manager/-/amqp-connection-manager-4.1.15.tgz#af1af653961c13674628d0b075eef7b95a209fe4" - integrity sha512-YGi8MuO2K4u0qC4b1qAKVIK8CdBJF+PpEZpHKxmrL3x61pU1sAb2SHzQl7ypoqr6oECIfVqtG9DGdOZxaUq1Wg== - dependencies: - promise-breaker "^6.0.0" - -amqplib@^0.10.7: - version "0.10.9" - resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.10.9.tgz#5b744c21d624f9307d0399e4d339b7354675831c" - integrity sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA== - dependencies: - buffer-more-ints "~1.0.0" - url-parse "~1.5.10" - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" - integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -ansi-styles@^6.1.0: - version "6.2.3" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" - integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== - -anymatch@^3.0.3, anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -apollo-datasource@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-3.3.2.tgz#5711f8b38d4b7b53fb788cb4dbd4a6a526ea74c8" - integrity sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg== - dependencies: - "@apollo/utils.keyvaluecache" "^1.0.1" - apollo-server-env "^4.2.1" - -apollo-reporting-protobuf@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.4.0.tgz#6edd31f09d4a3704d9e808d1db30eca2229ded26" - integrity sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog== - dependencies: - "@apollo/protobufjs" "1.2.6" - -apollo-server-core@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-3.13.0.tgz#ad6601fbb34cc97eedca27a9fb0b5738d11cd27d" - integrity sha512-v/g6DR6KuHn9DYSdtQijz8dLOkP78I5JSVJzPkARhDbhpH74QNwrQ2PP2URAPPEDJ2EeZNQDX8PvbYkAKqg+kg== - dependencies: - "@apollo/utils.keyvaluecache" "^1.0.1" - "@apollo/utils.logger" "^1.0.0" - "@apollo/utils.usagereporting" "^1.0.0" - "@apollographql/apollo-tools" "^0.5.3" - "@apollographql/graphql-playground-html" "1.6.29" - "@graphql-tools/mock" "^8.1.2" - "@graphql-tools/schema" "^8.0.0" - "@josephg/resolvable" "^1.0.0" - apollo-datasource "^3.3.2" - apollo-reporting-protobuf "^3.4.0" - apollo-server-env "^4.2.1" - apollo-server-errors "^3.3.1" - apollo-server-plugin-base "^3.7.2" - apollo-server-types "^3.8.0" - async-retry "^1.2.1" - fast-json-stable-stringify "^2.1.0" - graphql-tag "^2.11.0" - loglevel "^1.6.8" - lru-cache "^6.0.0" - node-abort-controller "^3.0.1" - sha.js "^2.4.11" - uuid "^9.0.0" - whatwg-mimetype "^3.0.0" - -apollo-server-env@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-4.2.1.tgz#ea5b1944accdbdba311f179e4dfaeca482c20185" - integrity sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g== - dependencies: - node-fetch "^2.6.7" - -apollo-server-errors@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz#ba5c00cdaa33d4cbd09779f8cb6f47475d1cd655" - integrity sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA== - -apollo-server-express@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-3.13.0.tgz#0d8d9bbba3b8b8264912d215f63fd44e74d5f42a" - integrity sha512-iSxICNbDUyebOuM8EKb3xOrpIwOQgKxGbR2diSr4HP3IW8T3njKFOoMce50vr+moOCe1ev8BnLcw9SNbuUtf7g== - dependencies: - "@types/accepts" "^1.3.5" - "@types/body-parser" "1.19.2" - "@types/cors" "2.8.12" - "@types/express" "4.17.14" - "@types/express-serve-static-core" "4.17.31" - accepts "^1.3.5" - apollo-server-core "^3.13.0" - apollo-server-types "^3.8.0" - body-parser "^1.19.0" - cors "^2.8.5" - parseurl "^1.3.3" - -apollo-server-plugin-base@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-3.7.2.tgz#c19cd137bc4c993ba2490ba2b571b0f3ce60a0cd" - integrity sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw== - dependencies: - apollo-server-types "^3.8.0" - -apollo-server-types@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/apollo-server-types/-/apollo-server-types-3.8.0.tgz#d976b6967878681f715fe2b9e4dad9ba86b1346f" - integrity sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A== - dependencies: - "@apollo/utils.keyvaluecache" "^1.0.1" - "@apollo/utils.logger" "^1.0.0" - apollo-reporting-protobuf "^3.4.0" - apollo-server-env "^4.2.1" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" - integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== - dependencies: - call-bound "^1.0.3" - is-array-buffer "^3.0.5" - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -array-includes@^3.1.9: - version "3.1.9" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" - integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-abstract "^1.24.0" - es-object-atoms "^1.1.1" - get-intrinsic "^1.3.0" - is-string "^1.1.1" - math-intrinsics "^1.1.0" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.findlastindex@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" - integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-abstract "^1.23.9" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - es-shim-unscopables "^1.1.0" - -array.prototype.flat@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" - integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" - -array.prototype.flatmap@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" - integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" - -arraybuffer.prototype.slice@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" - integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== - dependencies: - array-buffer-byte-length "^1.0.1" - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - is-array-buffer "^3.0.4" - -arrify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== - -asn1.js@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -ast-types@^0.13.4: - version "0.13.4" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" - integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== - dependencies: - tslib "^2.0.1" - -async-function@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" - integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== - -async-retry@^1.2.1, async-retry@^1.2.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" - integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== - dependencies: - retry "0.13.1" - -async@3.2.5: - version "3.2.5" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" - integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== - -async@^3.2.3: - version "3.2.6" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" - integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -auto-parse@^1.3.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/auto-parse/-/auto-parse-1.8.0.tgz#1164051c832d5574e8eb6a18d3a37a278fe61e72" - integrity sha512-Uri4uC+K5cSi5hjM4snFrqPrjqUpwxeSW5EMTPvN7Ju3PlDzmXXDr5tjdzxPvvwgT3J7bmMDJ3Rm625nbrc72A== - dependencies: - typpy "2.3.11" - -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -aws-sdk@^2.378.0: - version "2.1692.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1692.0.tgz#9dac5f7bfcc5ab45825cc8591b12753aa7d2902c" - integrity sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw== - dependencies: - buffer "4.9.2" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.16.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - util "^0.12.4" - uuid "8.0.0" - xml2js "0.6.2" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws-ssl-profiles@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641" - integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g== - -aws4@1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== - -aws4@^1.12.0: - version "1.13.2" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" - integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== - -axios@^1.12.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.0.tgz#ead6f495f41f9c8869dcf7b0f24f5a4ab89707f0" - integrity sha512-zt40Pz4zcRXra9CVV31KeyofwiNvAbJ5B6YPz9pMJ+yOSLikvPT4Yi5LjfgjRa9CawVYBaD1JQzIVcIvBejKeA== - dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.4" - proxy-from-env "^1.1.0" - -babel-eslint@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" - integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.0" - "@babel/traverse" "^7.7.0" - "@babel/types" "^7.7.0" - eslint-visitor-keys "^1.0.0" - resolve "^1.12.0" - -babel-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" - integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== - dependencies: - "@jest/transform" "^29.7.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.6.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== - 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" - -babel-plugin-jest-hoist@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" - integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - -babel-preset-current-node-syntax@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" - integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== - dependencies: - "@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" - -babel-preset-jest@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" - integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== - dependencies: - babel-plugin-jest-hoist "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - -babel-runtime@6.x.x: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -baseline-browser-mapping@^2.8.19: - version "2.8.20" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz#6766cf270f3668d20b6712b9c54cc911b87da714" - integrity sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ== - -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - -basic-ftp@^5.0.2: - version "5.0.5" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" - integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -bignumber.js@^9.0.0: - version "9.3.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.1.tgz#759c5aaddf2ffdc4f154f7b493e1c8770f88c4d7" - integrity sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ== - -binary-extensions@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" - integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== - -bl@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" - integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - -bl@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bluebird@^2.6.2: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - integrity sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ== - -bn.js@^4.0.0, bn.js@^4.11.9: - version "4.12.2" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.2.tgz#3d8fed6796c24e177737f7cc5172ee04ef39ec99" - integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== - -body-parser@1.20.3, body-parser@^1.18.2, body-parser@^1.19.0: - version "1.20.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - 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.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - -boolify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/boolify/-/boolify-1.0.1.tgz#b5c09e17cacd113d11b7bb3ed384cc012994d86b" - integrity sha512-ma2q0Tc760dW54CdOyJjhrg/a54317o1zYADQJFgperNGKIKgAUGIcKnuMiff8z57+yGlrGNEt4lPgZfCgTJgA== - -bowser@^2.11.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.12.1.tgz#f9ad78d7aebc472feb63dd9635e3ce2337e0e2c1" - integrity sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw== - -brace-expansion@^1.1.7: - version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" - integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.3, braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - -brotli@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" - integrity sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg== - dependencies: - base64-js "^1.1.2" - -browserslist@^4.24.0: - version "4.27.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.27.0.tgz#755654744feae978fbb123718b2f139bc0fa6697" - integrity sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw== - dependencies: - baseline-browser-mapping "^2.8.19" - caniuse-lite "^1.0.30001751" - electron-to-chromium "^1.5.238" - node-releases "^2.0.26" - update-browserslist-db "^1.1.4" - -bs-logger@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" - integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== - dependencies: - fast-json-stable-stringify "2.x" - -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - -buffer-equal-constant-time@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer-more-ints@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz#ef4f8e2dddbad429ed3828a9c55d44f05c611422" - integrity sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg== - -buffer@4.9.2: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -buffer@5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -busboy@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bind@^1.0.7, call-bind@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" - 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" - -call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase-keys@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" - integrity sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q== - dependencies: - camelcase "^4.1.0" - map-obj "^2.0.0" - quick-lru "^1.0.0" - -camelcase-keys@^9.1.0: - version "9.1.3" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-9.1.3.tgz#6367b2f9ec5724af541f58f0dcfee9b200022e5c" - integrity sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg== - dependencies: - camelcase "^8.0.0" - map-obj "5.0.0" - quick-lru "^6.1.1" - type-fest "^4.3.2" - -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw== - -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -camelcase@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-8.0.0.tgz#c0d36d418753fb6ad9c5e0437579745c1c14a534" - integrity sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA== - -camelize-ts@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelize-ts/-/camelize-ts-3.0.0.tgz#b9a7b4ff802464dc3d6475637a64a9742ad3db09" - integrity sha512-cgRwKKavoDKLTjO4FQTs3dRBePZp/2Y9Xpud0FhuCOTE86M2cniKN4CCXgRnsyXNMmQMifVHcv6SPaMtTx6ofQ== - -caniuse-lite@^1.0.30001751: - version "1.0.30001751" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz#dacd5d9f4baeea841641640139d2b2a4df4226ad" - integrity sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^4.0.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^5.3.0: - version "5.6.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" - integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - -chardet@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-2.0.0.tgz#24a74bd3954965ba2d30f490a915fc3cf053051f" - integrity sha512-xVgPpulCooDjY6zH4m9YW3jbkaBe3FKIAvF5sj5t7aBNsVl2ljIE+xwJ4iNgiDZHFQvNIpjdKdVOQvvk5ZfxbQ== - -"charenc@>= 0.0.1": - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== - -charset@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/charset/-/charset-1.0.1.tgz#8d59546c355be61049a8fa9164747793319852bd" - integrity sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg== - -check-disk-space@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/check-disk-space/-/check-disk-space-3.4.0.tgz#eb8e69eee7a378fd12e35281b8123a8b4c4a8ff7" - integrity sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw== - -chokidar@^3.5.2: - version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== - 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" - optionalDependencies: - fsevents "~2.3.2" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chromedriver@latest: - version "141.0.6" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-141.0.6.tgz#764fcd46fca9cd09187dd5461457395f2f1e0d2c" - integrity sha512-ccfB+3CuQVYGNOR0S3JWstAN/KIP7FUgCW0f2eGV8MJECJY6tSrGw7D7fG61y6XjvrKf3ylqg7KFP2dfboGd8A== - dependencies: - "@testim/chrome-version" "^1.1.4" - axios "^1.12.0" - compare-versions "^6.1.0" - extract-zip "^2.0.1" - proxy-agent "^6.4.0" - proxy-from-env "^1.1.0" - tcp-port-used "^1.0.2" - -ci-info@^3.2.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" - integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== - -cjs-module-lexer@^1.0.0, cjs-module-lexer@^1.2.2: - version "1.4.3" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" - integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== - -cli-progress@3.12.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.12.0.tgz#807ee14b66bcc086258e444ad0f19e7d42577942" - integrity sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A== - dependencies: - string-width "^4.2.3" - -cli-table3@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" - integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== - dependencies: - string-width "^4.2.0" - optionalDependencies: - "@colors/colors" "1.5.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone@2.x: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== - -cluster-key-slot@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" - integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" - integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-convert@^3.0.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-3.1.2.tgz#cef9e0fd4cb90b07c14697b3fa70af9d7f4870f1" - integrity sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg== - dependencies: - color-name "^2.0.0" - -color-name@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-2.0.2.tgz#85054825a23e6d6f81d3503f660c4c4a2a15f04f" - integrity sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-2.1.2.tgz#db1dd52414cc9037ada8fa7d936b8e9f6c3366c9" - integrity sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA== - dependencies: - color-name "^2.0.0" - -color@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/color/-/color-5.0.2.tgz#712ec894007ab27b37207732d182784e001b4a3d" - integrity sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA== - dependencies: - color-convert "^3.0.1" - color-string "^2.0.0" - -colorette@2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== - -colors@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -combined-stream@^1.0.8, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" - integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== - -commander@8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - -commander@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" - integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== - -commander@^12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" - integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== - -commander@^2.11.0, commander@^2.20.3: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - -common-tags@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" - integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== - -compare-versions@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.1.tgz#7af3cc1099ba37d244b3145a9af5201b629148a9" - integrity sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg== - -compressible@~2.0.18: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79" - integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== - dependencies: - 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" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -concat-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" - integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.0.2" - typedarray "^0.0.6" - -confusing-browser-globals@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" - integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== - -constant-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" - integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - upper-case "^2.0.2" - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== - -core-js@^2.4.0: - version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - -core-js@^3.33.0: - version "3.46.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.46.0.tgz#323a092b96381a9184d0cd49ee9083b2f93373bb" - integrity sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA== - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cors@^2.8.4, cors@^2.8.5: - version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - -create-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" - integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-config "^29.7.0" - jest-util "^29.7.0" - prompts "^2.0.1" - -cron@^3.1.7: - version "3.5.0" - resolved "https://registry.yarnpkg.com/cron/-/cron-3.5.0.tgz#e8caa384e998ed275e19598df4dbd0e8578c35d9" - integrity sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A== - dependencies: - "@types/luxon" "~3.4.0" - luxon "~3.5.0" - -cross-inspect@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b" - integrity sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A== - dependencies: - tslib "^2.4.0" - -cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -"crypt@>= 0.0.1": - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== - -cssfilter@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" - integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== - -csv-parse@4.16.3: - version "4.16.3" - resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" - integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - -data-uri-to-buffer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" - integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== - -data-uri-to-buffer@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" - integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== - -data-view-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" - integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-data-view "^1.0.2" - -data-view-byte-length@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" - integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-data-view "^1.0.2" - -data-view-byte-offset@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" - integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -date-format@^4.0.14: - version "4.0.14" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" - integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@4, debug@4.4.3, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -debug@4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - -dedent@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" - integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -degenerator@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" - integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== - dependencies: - ast-types "^0.13.4" - escodegen "^2.1.0" - esprima "^4.0.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -denque@^1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" - integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== - -denque@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" - integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== - -depd@2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -des.js@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" - integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -dlv@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" - integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dotenv-extended@^2.0.1: - version "2.9.0" - resolved "https://registry.yarnpkg.com/dotenv-extended/-/dotenv-extended-2.9.0.tgz#1e44a018f90ee88c066273cd855e3095c7e781e5" - integrity sha512-MKc4WCqZj6Abx4rpDbQ9LsuBJldRLxLgFkY5qE+4JM7hXVYT/v8zyWGgnBeDjSOGzEecWOFPlosNpxfB9YnsCw== - dependencies: - auto-parse "^1.3.0" - camelcase "^5.3.1" - cross-spawn "^7.0.1" - dotenv "^8.2.0" - -dotenv@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" - integrity sha512-XcaMACOr3JMVcEv0Y/iUM2XaOsATRZ3U1In41/1jjK6vJZ2PZbQ1bzCG8uvaByfaBpl9gqc9QWJovpUGBXLLYQ== - -dotenv@^8.2.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" - integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== - -dset@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" - integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== - -dunder-proto@^1.0.0, dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -duplexer@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -elasticsearch@^15.0.0: - version "15.5.0" - resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-15.5.0.tgz#62d9f238cf9e10b81c50a2303aa470c1f7da6b37" - integrity sha512-ZGKKaDkOFAap61ObBNkAxhYXCcAbRfkI4NVoSeLGnTD6/cItvY2j9LII/VV8/zclGe1x5m6DsVp47E4ze4aAeQ== - dependencies: - agentkeepalive "^3.4.1" - chalk "^1.0.0" - lodash "^4.17.10" - -electron-to-chromium@^1.5.238: - version "1.5.241" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.241.tgz#350a2cdcde43829d36af245a27167243e917c6ed" - integrity sha512-ILMvKX/ZV5WIJzzdtuHg8xquk2y0BOGlFOxBVwTpbiXqWIH0hamG45ddU4R3PQ0gYu+xgo0vdHXHli9sHIGb4w== - -elliptic@^6.6.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" - integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -enabled@2.0.x: - version "2.0.0" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" - integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.5" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" - integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== - dependencies: - once "^1.4.0" - -eol@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/eol/-/eol-0.10.0.tgz#51b35c6b9aa0329a26d102b6ddc454be8654739b" - integrity sha512-+w3ktYrOphcIqC1XKmhQYvM+o2uxgQFiimL7B6JPZJlWVxf7Lno9e/JWLPIgbHo7DoZ+b7jsf/NzrUcNe6ZTZQ== - -error-ex@^1.3.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" - integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0: - version "1.24.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" - integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== - dependencies: - array-buffer-byte-length "^1.0.2" - arraybuffer.prototype.slice "^1.0.4" - available-typed-arrays "^1.0.7" - call-bind "^1.0.8" - call-bound "^1.0.4" - data-view-buffer "^1.0.2" - data-view-byte-length "^1.0.2" - data-view-byte-offset "^1.0.1" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - es-set-tostringtag "^2.1.0" - es-to-primitive "^1.3.0" - function.prototype.name "^1.1.8" - get-intrinsic "^1.3.0" - get-proto "^1.0.1" - get-symbol-description "^1.1.0" - globalthis "^1.0.4" - gopd "^1.2.0" - has-property-descriptors "^1.0.2" - has-proto "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - internal-slot "^1.1.0" - is-array-buffer "^3.0.5" - is-callable "^1.2.7" - is-data-view "^1.0.2" - is-negative-zero "^2.0.3" - is-regex "^1.2.1" - is-set "^2.0.3" - is-shared-array-buffer "^1.0.4" - is-string "^1.1.1" - is-typed-array "^1.1.15" - is-weakref "^1.1.1" - math-intrinsics "^1.1.0" - object-inspect "^1.13.4" - object-keys "^1.1.1" - object.assign "^4.1.7" - own-keys "^1.0.1" - regexp.prototype.flags "^1.5.4" - safe-array-concat "^1.1.3" - safe-push-apply "^1.0.0" - safe-regex-test "^1.1.0" - set-proto "^1.0.0" - stop-iteration-iterator "^1.1.0" - string.prototype.trim "^1.2.10" - string.prototype.trimend "^1.0.9" - string.prototype.trimstart "^1.0.8" - typed-array-buffer "^1.0.3" - typed-array-byte-length "^1.0.3" - typed-array-byte-offset "^1.0.4" - typed-array-length "^1.0.7" - unbox-primitive "^1.1.0" - which-typed-array "^1.1.19" - -es-define-property@^1.0.0, es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== - dependencies: - es-errors "^1.3.0" - -es-set-tostringtag@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" - 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" - -es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" - integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== - dependencies: - hasown "^2.0.2" - -es-to-primitive@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" - integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== - dependencies: - is-callable "^1.2.7" - is-date-object "^1.0.5" - is-symbol "^1.0.4" - -escalade@^3.1.1, escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" - integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-airbnb-base@^15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236" - integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== - dependencies: - confusing-browser-globals "^1.0.10" - object.assign "^4.1.2" - object.entries "^1.1.5" - semver "^6.3.0" - -eslint-import-resolver-node@^0.3.9: - version "0.3.9" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" - integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== - dependencies: - debug "^3.2.7" - is-core-module "^2.13.0" - resolve "^1.22.4" - -eslint-module-utils@^2.12.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" - integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.7.0: - version "2.32.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980" - integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== - dependencies: - "@rtsao/scc" "^1.1.0" - array-includes "^3.1.9" - array.prototype.findlastindex "^1.2.6" - array.prototype.flat "^1.3.3" - array.prototype.flatmap "^1.3.3" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.12.1" - hasown "^2.0.2" - is-core-module "^2.16.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.fromentries "^2.0.8" - object.groupby "^1.0.3" - object.values "^1.2.1" - semver "^6.3.1" - string.prototype.trimend "^1.0.9" - tsconfig-paths "^3.15.0" - -eslint-scope@^7.1.1, eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint@^8.51.0, eslint@^8.56.0, eslint@^8.57.1: - version "8.57.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" - integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== - 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" - -esm@^3.2.25: - version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - -espree@^9.3.1, espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.0, esquery@^1.4.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -event-stream@=3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" - integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - -events@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== - -events@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - 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" - -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - -expect@^29.0.0, expect@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - -express@^4.16.1, express@^4.16.2: - version "4.21.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.3" - 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.13.0" - 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" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.2.9, fast-glob@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" - integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== - 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.8" - -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fast-safe-stringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" - integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== - -fast-xml-parser@5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz#4809fdfb1310494e341098c25cb1341a01a9144a" - integrity sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ== - dependencies: - strnum "^2.1.0" - -fastq@^1.6.0: - version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" - integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== - dependencies: - reusify "^1.0.4" - -fb-watchman@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" - integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== - dependencies: - bser "2.1.1" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - -fecha@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" - integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== - -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -file-type@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" - integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA== - -filesize@10.1.4: - version "10.1.4" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.1.4.tgz#184f256063a201f08b6e6b3cc47d21b60f5b8d89" - integrity sha512-ryBwPIIeErmxgPnm6cbESAzXjuEFubs+yKYLBZvg3CaiNcmkJChoOGcBSrZ6IwkMwPABwPpVXE6IlNdGJJrvEg== - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== - dependencies: - 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.1" - unpipe "~1.0.0" - -find-package-json@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/find-package-json/-/find-package-json-1.2.0.tgz#4057d1b943f82d8445fe52dc9cf456f6b8b58083" - integrity sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw== - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== - dependencies: - flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" - -flatted@3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" - integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== - -flatted@^3.2.7, flatted@^3.2.9: - version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" - integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== - -fn.name@1.x.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" - integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== - -follow-redirects@^1.15.6: - version "1.15.11" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" - integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== - -for-each@^0.3.3, for-each@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" - integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== - dependencies: - is-callable "^1.2.7" - -foreground-child@^3.1.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" - integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== - dependencies: - cross-spawn "^7.0.6" - signal-exit "^4.0.1" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" - integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== - 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" - -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -from@~0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== - -fs-capacitor@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-8.0.0.tgz#a95cbcf58dd50750fe718a03ec051961ef4e61f4" - integrity sha512-+Lk6iSKajdGw+7XYxUkwIzreJ2G1JFlYOdnKJv5PzwFLVsoJYBpCuS7WPIUSNT1IbQaEWT1nhYU63Ud03DyzLA== - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-extra@^11.2.0: - version "11.3.2" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.2.tgz#c838aeddc6f4a8c74dd15f85e11fe5511bfe02a4" - integrity sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -function.name@^1.0.3: - version "1.0.14" - resolved "https://registry.yarnpkg.com/function.name/-/function.name-1.0.14.tgz#76be1a4ad0dfa872d8025d55d6ca91767652bc67" - integrity sha512-s99L814NRuLxwF2sJMIcLhkQhueGXb3oKyvorzrUKKwlVB0SBbWrgZt4+EwKAo3ujCXnT7vshmCvXgZA09kCMw== - dependencies: - noop6 "^1.0.1" - -function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" - integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" - functions-have-names "^1.2.3" - hasown "^2.0.2" - is-callable "^1.2.7" - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -generate-function@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - -generator-function@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" - integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.1, get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - 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@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -get-stdin@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" - integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -get-symbol-description@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" - integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - -get-uri@^6.0.1: - version "6.0.5" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.5.tgz#714892aa4a871db671abc5395e5e9447bc306a16" - integrity sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg== - dependencies: - basic-ftp "^5.0.2" - data-uri-to-buffer "^6.0.2" - debug "^4.3.4" - -getopts@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" - integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - -git-up@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-7.0.0.tgz#bace30786e36f56ea341b6f69adfd83286337467" - integrity sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ== - dependencies: - is-ssh "^1.4.0" - parse-url "^8.1.0" - -git-url-parse@^13.1.0: - version "13.1.1" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-13.1.1.tgz#664bddf0857c6a75b3c1f0ae6239abb08a1486d4" - integrity sha512-PCFJyeSSdtnbfhSNRw9Wk96dDCNx+sogTe4YNXeXSJxt7xz5hvXekuRn9JX7m+Mf4OscCu8h+mtAl3+h5Fo8lQ== - dependencies: - git-up "^7.0.0" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob@^10.3.10: - version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== - dependencies: - 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" - -glob@^7.1.3, glob@^7.1.4: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" - -globalthis@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" - integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== - dependencies: - define-properties "^1.2.1" - gopd "^1.0.1" - -globby@^11.0.3, globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -gopd@^1.0.1, gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - -graphql-iso-date@^3.5.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/graphql-iso-date/-/graphql-iso-date-3.6.1.tgz#bd2d0dc886e0f954cbbbc496bbf1d480b57ffa96" - integrity sha512-AwFGIuYMJQXOEAgRlJlFL4H1ncFM8n8XmoVDTNypNOZyQ8LFDG2ppMFlsS862BSTCDcSUfHp8PD3/uJhv7t59Q== - -graphql-redis-subscriptions@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/graphql-redis-subscriptions/-/graphql-redis-subscriptions-2.7.0.tgz#955826c8cccdae0f684014c72e2f7c0bf677e178" - integrity sha512-IK4uCKx1UNhkcnG9lIqFWz9PpltSbuM8RygwGoB/e1HZMuKpAGeqqfHFeLKkQjjubvk4tAdUdx48AUkTAXJ17Q== - optionalDependencies: - ioredis "^5.3.2" - -graphql-sse@^2.5.4: - version "2.5.4" - resolved "https://registry.yarnpkg.com/graphql-sse/-/graphql-sse-2.5.4.tgz#29e93d8ccda58b9263b18f176b7be631638ec658" - integrity sha512-8PIGHQPv1vokYboL8KsoBomvyJCUMJCAwSp5f+bR2qVoYtPo7CYowrYV7LFiXuiirX0cxVzgUBFNa5BaI4rZ2A== - -graphql-subscriptions@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz#2142b2d729661ddf967b7388f7cf1dd4cf2e061d" - integrity sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g== - dependencies: - iterall "^1.3.0" - -graphql-tag@^2.11.0, graphql-tag@^2.12.6: - version "2.12.6" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" - integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== - dependencies: - tslib "^2.1.0" - -graphql-tools@^9.0.18: - version "9.0.20" - resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-9.0.20.tgz#a51024b29c0da9f057a42d9ddeaf153d142b7962" - integrity sha512-n9T4EEVzfJyQ0N3XAH4bMKhrEhyllBkfFq/4bpnEzkmkvYJ2D120Y5YslqYvpq0kohPFU706Q6zTHrHOfbL7MA== - dependencies: - "@graphql-tools/schema" "^10.0.25" - tslib "^2.4.0" - optionalDependencies: - "@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0" - -graphql-type-json@^0.3.0: - version "0.3.2" - resolved "https://registry.yarnpkg.com/graphql-type-json/-/graphql-type-json-0.3.2.tgz#f53a851dbfe07bd1c8157d24150064baab41e115" - integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg== - -graphql-upload@^17.0.0: - version "17.0.0" - resolved "https://registry.yarnpkg.com/graphql-upload/-/graphql-upload-17.0.0.tgz#a6052889e350db0cab4644da9c52ab4713a52f40" - integrity sha512-AI42S1UR1mdqg+LQ7KqGbrgcf4l9gpPu/R0drM4vSA5C94NfIjYyCeCdpktEledvZoAL8JURLLeB53++WACo1w== - dependencies: - "@types/busboy" "^1.5.0" - "@types/node" "*" - "@types/object-path" "^0.11.1" - busboy "^1.6.0" - fs-capacitor "^8.0.0" - http-errors "^2.0.0" - object-path "^0.11.8" - -graphql-ws@5.16.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.16.0.tgz#849efe02f384b4332109329be01d74c345842729" - integrity sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A== - -graphql@^16.10.0: - version "16.11.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" - integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== - -handlebars@4.7.8, handlebars@^4.7.8: - version "4.7.8" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" - integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.2" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - -has-bigints@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" - integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" - integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== - dependencies: - dunder-proto "^1.0.0" - -has-symbols@^1.0.3, has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -hoist-non-react-statics@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - -html-entities@^2.3.6: - version "2.6.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.6.0.tgz#7c64f1ea3b36818ccae3d3fb48b6974208e984f8" - integrity sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ== - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -http-errors@2.0.0, http-errors@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - 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" - -http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - -http-reasons@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/http-reasons/-/http-reasons-0.1.0.tgz#a953ca670078669dde142ce899401b9d6e85d3b4" - integrity sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ== - -http-signature@~1.3.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" - integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== - dependencies: - assert-plus "^1.0.0" - jsprim "^2.0.2" - sshpk "^1.14.1" - -httpntlm@1.8.13: - version "1.8.13" - resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.8.13.tgz#4130e555701b58207303bf757a4e1e41c06835bd" - integrity sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q== - dependencies: - des.js "^1.0.1" - httpreq ">=0.4.22" - js-md4 "^0.3.2" - underscore "~1.12.1" - -httpreq@>=0.4.22: - version "1.1.1" - resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-1.1.1.tgz#b8818316cdfd6b1bfb0f68b822fa1306cd24be68" - integrity sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw== - -https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.4, https-proxy-agent@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" - integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== - dependencies: - agent-base "^7.1.2" - debug "4" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@0.6.3, iconv-lite@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -iconv-lite@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" - integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -ieee754@1.1.13: - version "1.1.13" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -ieee754@^1.1.13, ieee754@^1.1.4: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore-by-default@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" - integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== - -ignore@^5.2.0, ignore@^5.2.4: - version "5.3.2" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" - integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== - -import-fresh@^3.2.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" - integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-in-the-middle@^1.13.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz#9e20827a322bbadaeb5e3bac49ea8f6d4685fdd8" - integrity sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA== - dependencies: - acorn "^8.14.0" - acorn-import-attributes "^1.9.5" - cjs-module-lexer "^1.2.2" - module-details-from-path "^1.0.3" - -import-local@^3.0.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" - integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -internal-slot@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" - integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== - dependencies: - es-errors "^1.3.0" - hasown "^2.0.2" - side-channel "^1.1.0" - -interpret@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== - -ioredis@^5.3.2: - version "5.8.2" - resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.8.2.tgz#c7a228a26cf36f17a5a8011148836877780e2e14" - integrity sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q== - dependencies: - "@ioredis/commands" "1.4.0" - 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" - -ip-address@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.0.1.tgz#a8180b783ce7788777d796286d61bce4276818ed" - integrity sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA== - -ip-regex@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" - integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-arguments@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b" - integrity sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA== - dependencies: - call-bound "^1.0.2" - has-tostringtag "^1.0.2" - -is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" - integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - get-intrinsic "^1.2.6" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-async-function@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" - integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== - dependencies: - async-function "^1.0.0" - call-bound "^1.0.3" - get-proto "^1.0.1" - has-tostringtag "^1.0.2" - safe-regex-test "^1.1.0" - -is-bigint@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" - integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== - dependencies: - has-bigints "^1.0.2" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" - integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.13.0, is-core-module@^2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" - integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== - dependencies: - hasown "^2.0.2" - -is-data-view@^1.0.1, is-data-view@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" - integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== - dependencies: - call-bound "^1.0.2" - get-intrinsic "^1.2.6" - is-typed-array "^1.1.13" - -is-date-object@^1.0.5, is-date-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" - integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== - dependencies: - call-bound "^1.0.2" - has-tostringtag "^1.0.2" - -is-extendable@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-finalizationregistry@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" - integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== - dependencies: - call-bound "^1.0.3" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-generator-function@^1.0.10, is-generator-function@^1.0.7: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" - integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== - dependencies: - call-bound "^1.0.4" - generator-function "^2.0.0" - get-proto "^1.0.1" - has-tostringtag "^1.0.2" - safe-regex-test "^1.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-invalid-path@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-invalid-path/-/is-invalid-path-1.0.2.tgz#2f84731559f4936abcf1b227632719cf45c5dc0e" - integrity sha512-6KLcFrPCEP3AFXMfnWrIFkZpYNBVzZAoBJJDEZKtI3LXkaDjM3uFMJQjxiizUuZTZ9Oh9FNv/soXbx5TcpaDmA== - -is-map@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" - integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== - -is-negative-zero@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" - integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== - -is-number-object@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" - integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== - -is-regex@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" - integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== - dependencies: - call-bound "^1.0.2" - gopd "^1.2.0" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - -is-set@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" - integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== - -is-shared-array-buffer@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" - integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== - dependencies: - call-bound "^1.0.3" - -is-ssh@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.1.tgz#76de1cdbe8f92a8b905d1a172b6bc09704c20396" - integrity sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg== - dependencies: - protocols "^2.0.1" - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-string@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" - integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-symbol@^1.0.4, is-symbol@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" - integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== - dependencies: - call-bound "^1.0.2" - has-symbols "^1.1.0" - safe-regex-test "^1.1.0" - -is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15, is-typed-array@^1.1.3: - version "1.1.15" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" - integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== - dependencies: - which-typed-array "^1.1.16" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-url@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" - integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== - -is-weakmap@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" - integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== - -is-weakref@^1.0.2, is-weakref@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" - integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== - dependencies: - call-bound "^1.0.3" - -is-weakset@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" - integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== - dependencies: - call-bound "^1.0.3" - get-intrinsic "^1.2.6" - -is2@^2.0.6: - version "2.0.9" - resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.9.tgz#ff63b441f90de343fa8fac2125ee170da8e8240d" - integrity sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g== - dependencies: - deep-is "^0.1.3" - ip-regex "^4.1.0" - is-url "^1.2.4" - -isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" - integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== - -istanbul-lib-instrument@^5.0.4: - version "5.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - 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" - -istanbul-lib-instrument@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" - integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== - dependencies: - "@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" - -istanbul-lib-report@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" - integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^4.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" - integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -iterall@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" - integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== - -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -jest-changed-files@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" - integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== - dependencies: - execa "^5.0.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - -jest-circus@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" - integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^1.0.0" - is-generator-fn "^2.0.0" - jest-each "^29.7.0" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - pretty-format "^29.7.0" - pure-rand "^6.0.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" - integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== - dependencies: - "@jest/core" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - chalk "^4.0.0" - create-jest "^29.7.0" - exit "^0.1.2" - import-local "^3.0.2" - jest-config "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - yargs "^17.3.1" - -jest-config@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" - integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.7.0" - "@jest/types" "^29.6.3" - babel-jest "^29.7.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.7.0" - jest-environment-node "^29.7.0" - jest-get-type "^29.6.3" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-runner "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-docblock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" - integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" - integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - jest-get-type "^29.6.3" - jest-util "^29.7.0" - pretty-format "^29.7.0" - -jest-environment-node@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" - integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -jest-get-type@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" - integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== - -jest-haste-map@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" - integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== - dependencies: - "@jest/types" "^29.6.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - jest-worker "^29.7.0" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" - integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== - dependencies: - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-util "^29.7.0" - -jest-pnp-resolver@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" - integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== - -jest-regex-util@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" - integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== - -jest-resolve-dependencies@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" - integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== - dependencies: - jest-regex-util "^29.6.3" - jest-snapshot "^29.7.0" - -jest-resolve@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" - integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.7.0" - jest-validate "^29.7.0" - resolve "^1.20.0" - resolve.exports "^2.0.0" - slash "^3.0.0" - -jest-runner@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" - integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== - dependencies: - "@jest/console" "^29.7.0" - "@jest/environment" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.7.0" - jest-environment-node "^29.7.0" - jest-haste-map "^29.7.0" - jest-leak-detector "^29.7.0" - jest-message-util "^29.7.0" - jest-resolve "^29.7.0" - jest-runtime "^29.7.0" - jest-util "^29.7.0" - jest-watcher "^29.7.0" - jest-worker "^29.7.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" - integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/globals" "^29.7.0" - "@jest/source-map" "^29.6.3" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" - integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.7.0" - graceful-fs "^4.2.9" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - natural-compare "^1.4.0" - pretty-format "^29.7.0" - semver "^7.5.3" - -jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" - integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== - 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" - -jest-watcher@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" - integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== - dependencies: - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.7.0" - string-length "^4.0.1" - -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" - integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== - dependencies: - "@jest/core" "^29.7.0" - "@jest/types" "^29.6.3" - import-local "^3.0.2" - jest-cli "^29.7.0" - -jmespath@0.16.0: - version "0.16.0" - resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" - integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== - -jose@4.14.4: - version "4.14.4" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" - integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g== - -js-md4@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/js-md4/-/js-md4-0.3.2.tgz#cd3b3dc045b0c404556c81ddb5756c23e59d7cf5" - integrity sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA== - -js-sha512@0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.9.0.tgz#ed569aa1e4bdaf0b83363c29db1ab87b1192d9ae" - integrity sha512-mirki9WS/SUahm+1TbAPkqvbCiCfOAAsyXeHxK1UkullnJVVqoJG2pL9ObvT05CN+tM7fxhfYm0NbXn+1hWoZg== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -jsesc@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== - -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json-stringify-safe@^5.0.0, json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -json5@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" - integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jsonschema@^1.4.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.5.0.tgz#f6aceb1ab9123563dd901d05f81f9d4883d3b7d8" - integrity sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw== - -jsonwebtoken@^8.0.1, jsonwebtoken@^8.5.1: - version "8.5.1" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== - dependencies: - jws "^3.2.2" - 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 "^5.6.0" - -jsprim@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" - integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - -jwa@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9" - integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== - dependencies: - buffer-equal-constant-time "^1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jwk-to-pem@^2.0.0: - version "2.0.7" - resolved "https://registry.yarnpkg.com/jwk-to-pem/-/jwk-to-pem-2.0.7.tgz#ceee3ad9d90206c525a9d02f1efe29e8c691178f" - integrity sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ== - dependencies: - asn1.js "^5.3.0" - elliptic "^6.6.1" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - -keycloak-connect@^26.1.1: - version "26.1.1" - resolved "https://registry.yarnpkg.com/keycloak-connect/-/keycloak-connect-26.1.1.tgz#27fb15a21ce7e63ea0d9842be019b65517a11eda" - integrity sha512-2wvNJXldB9Em+mp6liJ+AnftcJovFEvNhUgv3hblNDmVihBoBqn4zFlwLIN41lo0H8CicB2T86xZ5U2MiQ9FFA== - dependencies: - jwk-to-pem "^2.0.0" - optionalDependencies: - chromedriver latest - -keyv@^4.5.3: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -knex@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/knex/-/knex-3.1.0.tgz#b6ddd5b5ad26a6315234a5b09ec38dc4a370bd8c" - integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw== - dependencies: - colorette "2.0.19" - commander "^10.0.0" - debug "4.3.4" - escalade "^3.1.1" - esm "^3.2.25" - get-package-type "^0.1.0" - getopts "2.3.0" - interpret "^2.2.0" - lodash "^4.17.21" - pg-connection-string "2.6.2" - rechoir "^0.8.0" - resolve-from "^5.0.0" - tarn "^3.0.2" - tildify "2.0.0" - -kuler@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" - integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== - -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -liquid-json@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/liquid-json/-/liquid-json-0.3.1.tgz#9155a18136d8a6b2615e5f16f9a2448ab6b50eea" - integrity sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ== - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash-es@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" - integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== - -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== - -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== - -lodash.isarguments@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== - -lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log4js@^6.9.1: - version "6.9.1" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" - integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== - dependencies: - date-format "^4.0.14" - debug "^4.3.4" - flatted "^3.2.7" - rfdc "^1.3.0" - streamroller "^3.1.5" - -logform@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.7.0.tgz#cfca97528ef290f2e125a08396805002b2d060d1" - integrity sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ== - dependencies: - "@colors/colors" "1.6.0" - "@types/triple-beam" "^1.3.2" - fecha "^4.2.0" - ms "^2.1.1" - safe-stable-stringify "^2.3.1" - triple-beam "^1.3.0" - -loglevel-colored-level-prefix@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz#6a40218fdc7ae15fc76c3d0f3e676c465388603e" - integrity sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA== - dependencies: - chalk "^1.1.3" - loglevel "^1.4.1" - -loglevel@^1.4.1, loglevel@^1.6.8: - version "1.9.2" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.2.tgz#c2e028d6c757720107df4e64508530db6621ba08" - integrity sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg== - -lokka@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/lokka/-/lokka-1.7.0.tgz#ab2e8334612d2afd359aa89047547bd7de21046c" - integrity sha512-4/XqX7LfkldHSDsHbl7alMdB0AINdsUTnqIcN6CwDa4srFpRR3iatx+6ULK2KDgS1JPKPDOTVuyhhoXZ+EgH7Q== - dependencies: - babel-runtime "6.x.x" - uuid "2.x.x" - -long@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -long@^5.0.0, long@^5.2.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" - integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== - -loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - -"lru-cache@7.10.1 - 7.13.1": - version "7.13.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4" - integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== - -lru-cache@^10.2.0, lru-cache@^10.4.3: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lru-cache@^7.14.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - -lru.min@^1.0.0, lru.min@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/lru.min/-/lru.min-1.1.3.tgz#c8c3d001dfb4cbe5b8d1f4bea207d4a320e5d76f" - integrity sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q== - -luxon@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" - integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== - -make-dir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" - integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== - dependencies: - semver "^7.5.3" - -make-error@^1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -make-plural@^7.0.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.4.0.tgz#fa6990dd550dea4de6b20163f74e5ed83d8a8d6d" - integrity sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg== - -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - -map-obj@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-5.0.0.tgz#126c98596b63927d7360f287cccc67177aa1938b" - integrity sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA== - -map-obj@^2.0.0, map-obj@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" - integrity sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ== - -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" - integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== - -mariadb@^3.4.5: - version "3.4.5" - resolved "https://registry.yarnpkg.com/mariadb/-/mariadb-3.4.5.tgz#27ccfcb4fab3d7805ec4e365ff61f76fe7bd8c70" - integrity sha512-gThTYkhIS5rRqkVr+Y0cIdzr+GRqJ9sA2Q34e0yzmyhMCwyApf3OKAC1jnF23aSlIOqJuyaUFUcj7O1qZslmmQ== - dependencies: - "@types/geojson" "^7946.0.16" - "@types/node" "^24.0.13" - denque "^2.1.0" - iconv-lite "^0.6.3" - lru-cache "^10.4.3" - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -merge-descriptors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" - integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^4.0.4, micromatch@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" - integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -"mime-db@>= 1.43.0 < 2": - version "1.54.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== - -mime-format@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mime-format/-/mime-format-2.0.1.tgz#1274876d58bc803332427a515f5f7036e07b9413" - integrity sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg== - dependencies: - charset "^1.0.0" - -mime-types@2.1.35, mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - -mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - -mkdirp@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" - integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== - -module-details-from-path@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz#b662fdcd93f6c83d3f25289da0ce81c8d9685b94" - integrity sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w== - -moment@^2.24.0: - version "2.30.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" - integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== - -moo@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" - integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== - -morgan@^1.9.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.1.tgz#4e02e6a4465a48e26af540191593955d17f61570" - integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.1.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -mysql2@^3.15.3: - version "3.15.3" - resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.15.3.tgz#f0348d9c7401bb98cb1f45ffc5a773b109f70808" - integrity sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg== - dependencies: - aws-ssl-profiles "^1.1.1" - denque "^2.1.0" - generate-function "^2.3.1" - iconv-lite "^0.7.0" - long "^5.2.1" - lru.min "^1.0.0" - named-placeholders "^1.1.3" - seq-queue "^0.0.5" - sqlstring "^2.3.2" - -named-placeholders@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.4.tgz#92882114525181077e21aab96889f25e857e233a" - integrity sha512-/qfG0Kk/bLJIvej4FcPQ2KYUJP8iQdU1CTxysNb/U2wUNb+/4K485yeio8iNoiwfqJnsTInXoRPTza0dZWHVJQ== - dependencies: - lru.min "^1.1.0" - -nan@^2.22.2: - version "2.23.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.23.0.tgz#24aa4ddffcc37613a2d2935b97683c1ec96093c6" - integrity sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -negotiator@~0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" - integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== - -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -netmask@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" - integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== - -newman@^6.1.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/newman/-/newman-6.2.1.tgz#138f1c60e973dd6ab126df10c97bf1521a129e50" - integrity sha512-Zq8Sr5GFF+OXs5yIbyglLMKMh1WNMjYVV0yZaSBZ+DIgQOIWcxT8QTfbrl/YUGrLyT4rjpu+yZ/Z+kozw79GEA== - dependencies: - "@postman/tough-cookie" "4.1.3-postman.1" - async "3.2.5" - chardet "2.0.0" - cli-progress "3.12.0" - cli-table3 "0.6.5" - colors "1.4.0" - commander "11.1.0" - csv-parse "4.16.3" - filesize "10.1.4" - liquid-json "0.3.1" - lodash "4.17.21" - mkdirp "3.0.1" - postman-collection "4.4.0" - postman-collection-transformer "4.1.8" - postman-request "2.88.1-postman.34" - postman-runtime "7.39.1" - pretty-ms "7.0.1" - semver "7.6.3" - serialised-error "1.1.3" - word-wrap "1.2.5" - xmlbuilder "15.1.1" - -newrelic@^12.5.0: - version "12.25.0" - resolved "https://registry.yarnpkg.com/newrelic/-/newrelic-12.25.0.tgz#31260f8ba48408f0cc52755245b973883a6dd2f1" - integrity sha512-WqYv7EvOcOQhvnMbPvjhZzcihO36qj2iNOO6jTfaixku+f558KFawXdNjw3Gl3kL1ycrTfO7N0ywrIZ1ggKGbA== - dependencies: - "@grpc/grpc-js" "^1.13.2" - "@grpc/proto-loader" "^0.7.5" - "@newrelic/security-agent" "^2.4.2" - "@opentelemetry/api" "^1.9.0" - "@opentelemetry/core" "^2.0.0" - "@opentelemetry/exporter-metrics-otlp-proto" "^0.201.1" - "@opentelemetry/resources" "^2.0.1" - "@opentelemetry/sdk-metrics" "^2.0.1" - "@opentelemetry/sdk-trace-base" "^2.0.0" - "@tyriar/fibonacci-heap" "^2.0.7" - concat-stream "^2.0.0" - https-proxy-agent "^7.0.1" - import-in-the-middle "^1.13.0" - json-bigint "^1.0.0" - json-stringify-safe "^5.0.0" - module-details-from-path "^1.0.3" - readable-stream "^3.6.1" - require-in-the-middle "^7.4.0" - semver "^7.5.2" - winston-transport "^4.5.0" - optionalDependencies: - "@newrelic/fn-inspect" "^4.4.0" - "@newrelic/native-metrics" "^11.1.0" - "@prisma/prisma-fmt-wasm" "^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085" - -nmtree@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/nmtree/-/nmtree-1.0.6.tgz#953e057ad545e9e627f1275bd25fea4e92c1cf63" - integrity sha512-SUPCoyX5w/lOT6wD/PZEymR+J899984tYEOYjuDqQlIOeX5NSb1MEsCcT0az+dhZD0MLAj5hGBZEpKQxuDdniA== - dependencies: - commander "^2.11.0" - -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - -node-abi@^3.3.0: - version "3.78.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.78.0.tgz#fd0ecbd0aa89857b98da06bd3909194abb0821ba" - integrity sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ== - dependencies: - semver "^7.3.5" - -node-abort-controller@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" - integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== - -node-cache@*: - version "5.1.2" - resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" - integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== - dependencies: - clone "2.x" - -node-cleanup@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c" - integrity sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw== - -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - -node-fetch@^2.6.7: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" - integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - -node-forge@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - -node-gyp-build@^4.8.1: - version "4.8.4" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" - integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== - -node-oauth1@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/node-oauth1/-/node-oauth1-1.3.0.tgz#736d29f3c5fce2713ce1552c8beccb2a60df7b6f" - integrity sha512-0yggixNfrA1KcBwvh/Hy2xAS1Wfs9dcg6TdFf2zN7gilcAigMdrtZ4ybrBSXBgLvGDw9V1p2MRnGBMq7XjTWLg== - -node-releases@^2.0.26: - version "2.0.26" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.26.tgz#fdfa272f2718a1309489d18aef4ef5ba7f5dfb52" - integrity sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA== - -nodemon@^3.0.3: - version "3.1.10" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.10.tgz#5015c5eb4fffcb24d98cf9454df14f4fecec9bc1" - integrity sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw== - 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" - -noop6@^1.0.1: - version "1.0.10" - resolved "https://registry.yarnpkg.com/noop6/-/noop6-1.0.10.tgz#beb17db586b684585eb955de502506a069c817d7" - integrity sha512-WZvuCILZFZHK+WuqCQwxLBGllkBK1ct8s8Mu9FMDbEsBE6/bqNxyFGbX7Xky+6bYFL8X2Ou4Cis4CJyrwXLvQA== - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-run-path@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-3.1.0.tgz#7f91be317f6a466efed3c9f2980ad8a4ee8b0fa5" - integrity sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg== - dependencies: - path-key "^3.0.0" - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-hash@^1.1.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" - integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== - -object-inspect@^1.13.3, object-inspect@^1.13.4: - version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-path@^0.11.8: - version "0.11.8" - resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.8.tgz#ed002c02bbdd0070b78a27455e8ae01fc14d4742" - integrity sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA== - -object.assign@^4.1.2, object.assign@^4.1.7: - version "4.1.7" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" - integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - has-symbols "^1.1.0" - object-keys "^1.1.1" - -object.entries@^1.1.5: - version "1.1.9" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3" - integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-object-atoms "^1.1.1" - -object.fromentries@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" - integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - -object.groupby@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" - integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - -object.values@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" - integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -on-headers@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" - integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -one-time@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" - integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== - dependencies: - fn.name "1.x.x" - -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -optimism@^0.18.0: - version "0.18.1" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.1.tgz#5cf16847921413dbb0ac809907370388b9c6335f" - integrity sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ== - dependencies: - "@wry/caches" "^1.0.0" - "@wry/context" "^0.7.0" - "@wry/trie" "^0.5.0" - tslib "^2.3.0" - -optionator@^0.9.3: - version "0.9.4" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" - integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.5" - -own-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" - integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== - dependencies: - get-intrinsic "^1.2.6" - object-keys "^1.1.1" - safe-push-apply "^1.0.0" - -p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pac-proxy-agent@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz#9cfaf33ff25da36f6147a20844230ec92c06e5df" - integrity sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA== - dependencies: - "@tootallnate/quickjs-emscripten" "^0.23.0" - agent-base "^7.1.2" - debug "^4.3.4" - get-uri "^6.0.1" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.6" - pac-resolver "^7.0.1" - socks-proxy-agent "^8.0.5" - -pac-resolver@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" - integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== - dependencies: - degenerator "^5.0.0" - netmask "^2.0.2" - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-json@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse-ms@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" - integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== - -parse-path@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-7.1.0.tgz#41fb513cb122831807a4c7b29c8727947a09d8c6" - integrity sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw== - dependencies: - protocols "^2.0.0" - -parse-url@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-8.1.0.tgz#972e0827ed4b57fc85f0ea6b0d839f0d8a57a57d" - integrity sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w== - dependencies: - parse-path "^7.0.0" - -parseurl@^1.3.3, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-to-regexp@0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" - integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== - dependencies: - through "~2.3" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - -performance-now@2.1.0, performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - -pg-connection-string@2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475" - integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pirates@^4.0.4: - version "4.0.7" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" - integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -possible-typed-array-names@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" - integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== - -postman-collection-transformer@4.1.8: - version "4.1.8" - resolved "https://registry.yarnpkg.com/postman-collection-transformer/-/postman-collection-transformer-4.1.8.tgz#ba6b7468945a604c7b3121f03bc7c61a2f72ca15" - integrity sha512-smJ6X7Z7kbg6hp7JZPFixrSN3J3WkQed7DrWCC5tF7IxOMpFLqhtTtGssY8nD1inP8+mJf+N72Pf2ttUAHgBKw== - dependencies: - commander "8.3.0" - inherits "2.0.4" - lodash "4.17.21" - semver "7.5.4" - strip-json-comments "3.1.1" - -postman-collection@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.4.0.tgz#6acb6e3796fcd9f6ac5a94e6894185e42387d7da" - integrity sha512-2BGDFcUwlK08CqZFUlIC8kwRJueVzPjZnnokWPtJCd9f2J06HBQpGL7t2P1Ud1NEsK9NHq9wdipUhWLOPj5s/Q== - dependencies: - "@faker-js/faker" "5.5.3" - file-type "3.9.0" - http-reasons "0.1.0" - iconv-lite "0.6.3" - liquid-json "0.3.1" - lodash "4.17.21" - mime-format "2.0.1" - mime-types "2.1.35" - postman-url-encoder "3.0.5" - semver "7.5.4" - uuid "8.3.2" - -postman-request@2.88.1-postman.34: - version "2.88.1-postman.34" - resolved "https://registry.yarnpkg.com/postman-request/-/postman-request-2.88.1-postman.34.tgz#a5f2c42649935008f87d5aa185c7035d4948cf65" - integrity sha512-GkolJ4cIzgamcwHRDkeZc/taFWO1u2HuGNML47K9ZAsFH2LdEkS5Yy8QanpzhjydzV3WWthl9v60J8E7SjKodQ== - dependencies: - "@postman/form-data" "~3.1.1" - "@postman/tough-cookie" "~4.1.3-postman.1" - "@postman/tunnel-agent" "^0.6.3" - aws-sign2 "~0.7.0" - aws4 "^1.12.0" - brotli "^1.3.3" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - har-validator "~5.1.3" - http-signature "~1.3.1" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "^2.1.35" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.3" - safe-buffer "^5.1.2" - stream-length "^1.0.2" - uuid "^8.3.2" - -postman-runtime@7.39.1: - version "7.39.1" - resolved "https://registry.yarnpkg.com/postman-runtime/-/postman-runtime-7.39.1.tgz#20c07b51500a9200125a23a020300c9c60fe7693" - integrity sha512-IRNrBE0l1K3ZqQhQVYgF6MPuqOB9HqYncal+a7RpSS+sysKLhJMkC9SfUn1HVuOpokdPkK92ykvPzj8kCOLYAg== - dependencies: - "@postman/tough-cookie" "4.1.3-postman.1" - async "3.2.5" - aws4 "1.12.0" - handlebars "4.7.8" - httpntlm "1.8.13" - jose "4.14.4" - js-sha512 "0.9.0" - lodash "4.17.21" - mime-types "2.1.35" - node-forge "1.3.1" - node-oauth1 "1.3.0" - performance-now "2.1.0" - postman-collection "4.4.0" - postman-request "2.88.1-postman.34" - postman-sandbox "4.7.1" - postman-url-encoder "3.0.5" - serialised-error "1.1.3" - strip-json-comments "3.1.1" - uuid "8.3.2" - -postman-sandbox@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/postman-sandbox/-/postman-sandbox-4.7.1.tgz#e0f48d84f1725c7d71dfbc924ff142a338f0a45b" - integrity sha512-H2wYSLK0mB588IaxoLrLoPbpmxsIcwFtgaK2c8gAsAQ+TgYFePwb4qdeVcYDMqmwrLd77/ViXkjasP/sBMz1sQ== - dependencies: - lodash "4.17.21" - postman-collection "4.4.0" - teleport-javascript "1.0.0" - uvm "2.1.1" - -postman-url-encoder@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz#af2efee3bb7644e2b059d8a78bc8070fae0467a5" - integrity sha512-jOrdVvzUXBC7C+9gkIkpDJ3HIxOHTIqjpQ4C1EMt1ZGeMvSEpbFCKq23DEfgsj46vMnDgyQf+1ZLp2Wm+bKSsA== - dependencies: - punycode "^2.1.1" - -prebuildify@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/prebuildify/-/prebuildify-6.0.1.tgz#655746f91fc95b68610615898678536dd303cd03" - integrity sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw== - dependencies: - minimist "^1.2.5" - mkdirp-classic "^0.5.3" - node-abi "^3.3.0" - npm-run-path "^3.1.0" - pump "^3.0.0" - tar-fs "^2.1.0" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prettier-eslint-cli@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/prettier-eslint-cli/-/prettier-eslint-cli-8.0.1.tgz#112a9002b816d13a96f1bb1eb4310952e26cd4f0" - integrity sha512-jru4JUDHzWEtM/SOxqagU7hQTVP8BVrxO2J0qNauWZuPRld6Ea2eyNaEzIGx6I+yjmOLCsjNM+vU1AJgaW1ZSQ== - dependencies: - "@messageformat/core" "^3.2.0" - "@prettier/eslint" "npm:prettier-eslint@^16.1.0" - arrify "^2.0.1" - boolify "^1.0.1" - camelcase-keys "^9.1.0" - chalk "^4.1.2" - common-tags "^1.8.2" - core-js "^3.33.0" - eslint "^8.51.0" - find-up "^5.0.0" - get-stdin "^8.0.0" - glob "^10.3.10" - ignore "^5.2.4" - indent-string "^4.0.0" - lodash.memoize "^4.1.2" - loglevel-colored-level-prefix "^1.0.0" - rxjs "^7.8.1" - yargs "^17.7.2" - -prettier@^3.2.5, prettier@^3.5.3: - version "3.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" - integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== - -pretty-bytes@^5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== - -pretty-format@^29.0.0, pretty-format@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-ms@7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" - integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== - dependencies: - parse-ms "^2.1.0" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -promise-breaker@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/promise-breaker/-/promise-breaker-6.0.0.tgz#107d2b70f161236abdb4ac5a736c7eb8df489d0f" - integrity sha512-BthzO9yTPswGf7etOBiHCVuugs2N01/Q/94dIPls48z2zCmrnDptUUZzfIb+41xq0MnYZ/BzmOd6ikDR4ibNZA== - -prompts@^2.0.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -prop-types@^15.7.2: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -protobufjs@^7.2.5, protobufjs@^7.3.0, protobufjs@^7.5.3: - version "7.5.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a" - integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - -protocols@^2.0.0, protocols@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.2.tgz#822e8fcdcb3df5356538b3e91bfd890b067fd0a4" - integrity sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ== - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -proxy-agent@^6.4.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.5.0.tgz#9e49acba8e4ee234aacb539f89ed9c23d02f232d" - integrity sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A== - dependencies: - agent-base "^7.1.2" - debug "^4.3.4" - http-proxy-agent "^7.0.1" - https-proxy-agent "^7.0.6" - lru-cache "^7.14.1" - pac-proxy-agent "^7.1.0" - proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.5" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -ps-tree@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" - integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== - dependencies: - event-stream "=3.3.4" - -psl@^1.1.33: - version "1.15.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" - integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== - dependencies: - punycode "^2.3.1" - -pstree.remy@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" - integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== - -pump@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" - integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== - -punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -pure-rand@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" - integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== - -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - -qs@~6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -quick-lru@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" - integrity sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA== - -quick-lru@^6.1.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-6.1.2.tgz#e9a90524108629be35287d0b864e7ad6ceb3659e" - integrity sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ== - -ramda@0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" - integrity sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ== - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -react-is@^16.13.1, react-is@^16.7.0: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^18.0.0: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - -readable-stream@^2.3.5: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.1, readable-stream@^3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - -redis-commands@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" - integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== - -redis-errors@^1.0.0, redis-errors@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" - integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== - -redis-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" - integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== - dependencies: - redis-errors "^1.0.0" - -redis@^3.0.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/redis/-/redis-3.1.2.tgz#766851117e80653d23e0ed536254677ab647638c" - integrity sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw== - dependencies: - denque "^1.5.0" - redis-commands "^1.7.0" - redis-errors "^1.2.0" - redis-parser "^3.0.0" - -reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" - integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.9" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.7" - get-proto "^1.0.1" - which-builtin-type "^1.2.1" - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regexp.prototype.flags@^1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" - integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-errors "^1.3.0" - get-proto "^1.0.1" - gopd "^1.2.0" - set-function-name "^2.0.2" - -rehackt@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" - integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== - -request-ip@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-3.3.0.tgz#863451e8fec03847d44f223e30a5d63e369fa611" - integrity sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-in-the-middle@^7.4.0: - version "7.5.2" - resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz#dc25b148affad42e570cf0e41ba30dc00f1703ec" - integrity sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ== - dependencies: - debug "^4.3.5" - module-details-from-path "^1.0.3" - resolve "^1.22.8" - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -require-relative@^0.8.7: - version "0.8.7" - resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" - integrity sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@5.0.0, resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve.exports@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" - integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== - -resolve@^1.12.0, resolve@^1.20.0, resolve@^1.22.4, resolve@^1.22.8: - version "1.22.11" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" - integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== - dependencies: - is-core-module "^2.16.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -retry@0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - -reusify@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" - integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== - -rfdc@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" - integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -ringbufferjs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ringbufferjs/-/ringbufferjs-2.0.0.tgz#09f40e2675a99cfef430b7ec5815ac1bc2e24120" - integrity sha512-GCOqTzUsTHF7nrqcgtNGAFotXztLgiePpIDpyWZ7R5I02tmfJWV+/yuJc//Hlsd8G+WzI1t/dc2y/w2imDZdog== - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -rxjs@^7.8.1: - version "7.8.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" - integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== - dependencies: - tslib "^2.1.0" - -safe-array-concat@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" - integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - get-intrinsic "^1.2.6" - has-symbols "^1.1.0" - isarray "^2.0.5" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-identifier@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" - integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== - -safe-push-apply@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" - integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== - dependencies: - es-errors "^1.3.0" - isarray "^2.0.5" - -safe-regex-test@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" - integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - is-regex "^1.2.1" - -safe-stable-stringify@^2.3.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" - integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== - -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sax@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" - integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== - -sax@>=0.6.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" - integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== - -semver@7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@7.6.3: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - -semver@^5.6.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.3.5, semver@^7.3.6, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3, semver@^7.7.3: - version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" - integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== - -send@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -seq-queue@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" - integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== - -serialised-error@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/serialised-error/-/serialised-error-1.1.3.tgz#8a4c466b29c26ff11016eaf1b5fa2b87ca4cd8b5" - integrity sha512-vybp3GItaR1ZtO2nxZZo8eOo7fnVaNtP3XE2vJKgzkKR2bagCkdJ1EpYYhEMd3qu/80DwQk9KjsNSxE3fXWq0g== - dependencies: - object-hash "^1.1.2" - stack-trace "0.0.9" - uuid "^3.0.0" - -serve-static@1.16.2: - version "1.16.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== - dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.19.0" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - -set-function-length@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - 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" - -set-function-name@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - -set-proto@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" - integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== - dependencies: - dunder-proto "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -sha.js@^2.4.11: - version "2.4.12" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.12.tgz#eb8b568bf383dfd1867a32c3f2b74eb52bdbf23f" - integrity sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w== - dependencies: - inherits "^2.0.4" - safe-buffer "^5.2.1" - to-buffer "^1.2.0" - -sha1@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848" - integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA== - dependencies: - charenc ">= 0.0.1" - crypt ">= 0.0.1" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - 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" - -side-channel@^1.0.6, side-channel@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== - dependencies: - 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" - -signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -simple-update-notifier@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" - integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== - dependencies: - semver "^7.5.3" - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -smart-buffer@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - -snakecase-keys@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/snakecase-keys/-/snakecase-keys-1.2.0.tgz#6c5d8f7a4dfda41afa86a26b6780c6daebb7e3b5" - integrity sha512-G5Faa3wQevGXcD5e4JKfmgofO+Fu4Jg4/nLyeZqWmBqVV0/3ORgervt3EjBi6PEFKhztPQWegZspteWnycx5dg== - dependencies: - map-obj "~2.0.0" - to-snake-case "~0.1.2" - -socks-proxy-agent@^8.0.5: - version "8.0.5" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee" - integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== - dependencies: - agent-base "^7.1.2" - debug "^4.3.4" - socks "^2.8.3" - -socks@^2.8.3: - version "2.8.7" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea" - integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A== - dependencies: - ip-address "^10.0.1" - smart-buffer "^4.2.0" - -sort-object-keys@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" - integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== - -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -split@0.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" - integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== - dependencies: - through "2" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -sqlstring@^2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" - integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== - -sshpk@^1.14.1: - version "1.18.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" - integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stack-trace@0.0.9: - version "0.0.9" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" - integrity sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ== - -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== - -stack-utils@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - -standard-as-callback@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" - integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -stop-iteration-iterator@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" - integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== - dependencies: - es-errors "^1.3.0" - internal-slot "^1.1.0" - -stream-browserify@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" - integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== - dependencies: - inherits "~2.0.4" - readable-stream "^3.5.0" - -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" - integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== - dependencies: - duplexer "~0.1.1" - -stream-length@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stream-length/-/stream-length-1.0.2.tgz#8277f3cbee49a4daabcfdb4e2f4a9b5e9f2c9f00" - integrity sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg== - dependencies: - bluebird "^2.6.2" - -streamroller@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" - integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== - dependencies: - date-format "^4.0.14" - debug "^4.3.4" - fs-extra "^8.1.0" - -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - -string-argv@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" - integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== - -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string.fromcodepoint@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz#8d978333c0bc92538f50f383e4888f3e5619d653" - integrity sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg== - -string.prototype.trim@^1.2.10: - version "1.2.10" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" - integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - define-data-property "^1.1.4" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-object-atoms "^1.0.0" - has-property-descriptors "^1.0.2" - -string.prototype.trimend@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" - integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -string.prototype.trimstart@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" - integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^7.0.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" - integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== - dependencies: - ansi-regex "^6.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@3.1.1, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -strnum@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.1.tgz#cf2a6e0cf903728b8b2c4b971b7e36b4e82d46ab" - integrity sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw== - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - -supports-color@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -symbol-observable@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" - integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== - -synp@^1.9.14: - version "1.9.14" - resolved "https://registry.yarnpkg.com/synp/-/synp-1.9.14.tgz#1feb222d273f6092c6c264746277e95655c60717" - integrity sha512-0e4u7KtrCrMqvuXvDN4nnHSEQbPlONtJuoolRWzut0PfuT2mEOvIFnYFHEpn5YPIOv7S5Ubher0b04jmYRQOzQ== - dependencies: - "@yarnpkg/lockfile" "^1.1.0" - colors "1.4.0" - commander "^7.2.0" - eol "^0.10.0" - fast-glob "^3.3.2" - lodash "4.17.21" - nmtree "^1.0.6" - semver "^7.6.3" - sort-object-keys "^1.1.3" - -tar-fs@^2.1.0: - version "2.1.4" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.4.tgz#800824dbf4ef06ded9afea4acafe71c67c76b930" - integrity sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tarn@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693" - integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ== - -tcp-port-used@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" - integrity sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA== - dependencies: - debug "4.3.1" - is2 "^2.0.6" - -teleport-javascript@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/teleport-javascript/-/teleport-javascript-1.0.0.tgz#c9397fad598d662027e4d3a5fa7e7da1c8361547" - integrity sha512-j1llvWVFyEn/6XIFDfX5LAU43DXe0GCt3NfXDwJ8XpRRMkS+i50SAkonAONBy+vxwPFBd50MFU8a2uj8R/ccLg== - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -through@2, through@~2.3, through@~2.3.1: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -tildify@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" - integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== - -tmpl@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - -to-buffer@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.2.2.tgz#ffe59ef7522ada0a2d1cb5dfe03bb8abc3cdc133" - integrity sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw== - dependencies: - isarray "^2.0.5" - safe-buffer "^5.2.1" - typed-array-buffer "^1.0.3" - -to-no-case@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-0.1.1.tgz#cf33c70e0f28168d95e4159abf150e8c542ef9fe" - integrity sha512-XNChsa36ssNQibYbv/kBSLb0jEfPuePdnbX1tBpHypUbtjBSR8ihaDRRYPZLMstjSpPM+onSgxeDjg+lgfIzZQ== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-snake-case@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/to-snake-case/-/to-snake-case-0.1.2.tgz#d047b6dbf048dae1bdc260b3c296f54ca34eed7c" - integrity sha512-0h2qEd1GZjWTbUqKRVo7D6ZdhM5H/2hUlA4g1+kQE4C27709WEVmbtAE+PQbYQ8iLgcyoHWMIN7fx6t8kg4Zkg== - dependencies: - to-space-case "0.1.2" - -to-space-case@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/to-space-case/-/to-space-case-0.1.2.tgz#9a66be3ebe53f2779f687f0262effd1fc5b6d15e" - integrity sha512-DtF9QZwx8W6WMhrCuvUZJYX9sT74/VOdVxi68EkEu3gt0SUQHhJG+UYbQI0559XcchsX7gziPlWuV4dEGC59dA== - dependencies: - to-no-case "0.1.1" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -touch@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" - integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -triple-beam@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" - integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== - -ts-api-utils@^1.0.1: - version "1.4.3" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" - integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== - -ts-invariant@^0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" - integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== - dependencies: - tslib "^2.1.0" - -ts-jest@^29.1.2: - version "29.4.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.5.tgz#a6b0dc401e521515d5342234be87f1ca96390a6f" - integrity sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q== - dependencies: - bs-logger "^0.2.6" - fast-json-stable-stringify "^2.1.0" - handlebars "^4.7.8" - json5 "^2.2.3" - lodash.memoize "^4.1.2" - make-error "^1.3.6" - semver "^7.7.3" - type-fest "^4.41.0" - yargs-parser "^21.1.1" - -tsc-silent@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tsc-silent/-/tsc-silent-1.2.2.tgz#414239cfc15cb70f8d2c29139f64b1c13d2daa11" - integrity sha512-XeNoMlREutyMotsQX7EijrgxXeyLscHUOV2W1goCZlEGvLH/7eFQ1snlPQf8cdQSdABqWEfvap0ab7s7FeCMrw== - dependencies: - "@types/node" "10.12.7" - yargs "12 - 15" - -tsc-watch@^6.0.4: - version "6.3.1" - resolved "https://registry.yarnpkg.com/tsc-watch/-/tsc-watch-6.3.1.tgz#b082cae71c599e3af18344cff31342ea17bc6f85" - integrity sha512-x1hNJ/m1Cln2OvNJJLPsnWI1pObm+Jq9jF6kyz0flEy/ym7Y9TvU9edg6lXLuGVkhDClwBWi8aWAFE0qgzEkzw== - dependencies: - cross-spawn "^7.0.3" - node-cleanup "^2.1.2" - ps-tree "^1.2.0" - string-argv "^0.3.1" - -tsconfig-paths@^3.15.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" - integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.2, tslib@^2.6.3, tslib@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^4.3.2, type-fest@^4.41.0: - version "4.41.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" - integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typed-array-buffer@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" - integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-typed-array "^1.1.14" - -typed-array-byte-length@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" - integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== - dependencies: - call-bind "^1.0.8" - for-each "^0.3.3" - gopd "^1.2.0" - has-proto "^1.2.0" - is-typed-array "^1.1.14" - -typed-array-byte-offset@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" - integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.8" - for-each "^0.3.3" - gopd "^1.2.0" - has-proto "^1.2.0" - is-typed-array "^1.1.15" - reflect.getprototypeof "^1.0.9" - -typed-array-length@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" - integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== - dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - is-typed-array "^1.1.13" - possible-typed-array-names "^1.0.0" - reflect.getprototypeof "^1.0.6" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - -typescript@^5.3.3: - version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" - integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== - -typpy@2.3.11: - version "2.3.11" - resolved "https://registry.yarnpkg.com/typpy/-/typpy-2.3.11.tgz#21a0d22c96fb646306e08b6c669ad43608e1b3b9" - integrity sha512-Jh/fykZSaxeKO0ceMAs6agki9T5TNA9kiIR6fzKbvafKpIw8UlNlHhzuqKyi5lfJJ5VojJOx9tooIbyy7vHV/g== - dependencies: - function.name "^1.0.3" - -uglify-js@^3.1.4: - version "3.19.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" - integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== - -unbox-primitive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" - integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== - dependencies: - call-bound "^1.0.3" - has-bigints "^1.0.2" - has-symbols "^1.1.0" - which-boxed-primitive "^1.1.1" - -undefsafe@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" - integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== - -underscore@~1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" - integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== - -undici-types@~7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" - integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== - -unescape-js@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/unescape-js/-/unescape-js-1.1.4.tgz#4bc6389c499cb055a98364a0b3094e1c3d5da395" - integrity sha512-42SD8NOQEhdYntEiUQdYq/1V/YHwr1HLwlHuTJB5InVVdOSbgI6xu8jK5q65yIzuFCfczzyDF/7hbGzVbyCw0g== - dependencies: - string.fromcodepoint "^0.2.1" - -unescape@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unescape/-/unescape-1.0.1.tgz#956e430f61cad8a4d57d82c518f5e6cc5d0dda96" - integrity sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ== - dependencies: - extend-shallow "^2.0.1" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -unixify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" - integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg== - dependencies: - normalize-path "^2.1.1" - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" - integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - -upper-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" - integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== - dependencies: - tslib "^2.0.3" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -url-join@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-5.0.0.tgz#c2f1e5cbd95fa91082a93b58a1f42fecb4bdbcf1" - integrity sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA== - -url-parse@^1.5.3, url-parse@~1.5.10: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url-template@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/url-template/-/url-template-3.1.1.tgz#c220d5f3f793d28b0de341002112879cc8a43905" - integrity sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA== - -url@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" - integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ== - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -util@^0.12.4: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/uuid4/-/uuid4-1.1.4.tgz#80fa0618749110bdb8aa47cc2cc2167c6331f4eb" - integrity sha512-Gr1q2k40LpF8CokcnQFjPDsdslzJbTCTBG5xQIEflUov431gFkY5KduiGIeKYAamkQnNn4IfdHJbLnl9Bib8TQ== - -uuid@2.x.x: - version "2.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" - integrity sha512-FULf7fayPdpASncVy4DLh3xydlXEJJpvIELjYjNeQWYUZ9pclcpvCZSr2gkmN2FrrGcI7G/cJsIEwk5/8vfXpg== - -uuid@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" - integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== - -uuid@8.3.2, uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -uuid@^3.0.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^9.0.0, uuid@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== - -uvm@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/uvm/-/uvm-2.1.1.tgz#4287db012666215700130a135b25771cae8083e1" - integrity sha512-BZ5w8adTpNNr+zczOBRpaX/hH8UPKAf7fmCnidrcsqt3bn8KT9bDIfuS7hgRU9RXgiN01su2pwysBONY6w8W5w== - dependencies: - flatted "3.2.6" - -v8-to-istanbul@^9.0.1: - version "9.3.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" - integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - -validator@^13.12.0: - version "13.15.20" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.20.tgz#054e9238109538a1bf46ae3e1290845a64fa2186" - integrity sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw== - -value-or-promise@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" - integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== - -value-or-promise@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" - integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== - -vary@^1, vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vue-eslint-parser@^9.4.3: - version "9.4.3" - resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz#9b04b22c71401f1e8bca9be7c3e3416a4bde76a8" - integrity sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg== - dependencies: - debug "^4.3.4" - eslint-scope "^7.1.1" - eslint-visitor-keys "^3.3.0" - espree "^9.3.1" - esquery "^1.4.0" - lodash "^4.17.21" - semver "^7.3.6" - -walker@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" - -web-streams-polyfill@^3.0.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" - integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-mimetype@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" - integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" - integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== - dependencies: - is-bigint "^1.1.0" - is-boolean-object "^1.2.1" - is-number-object "^1.1.1" - is-string "^1.1.1" - is-symbol "^1.1.1" - -which-builtin-type@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" - integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== - dependencies: - call-bound "^1.0.2" - function.prototype.name "^1.1.6" - has-tostringtag "^1.0.2" - is-async-function "^2.0.0" - is-date-object "^1.1.0" - is-finalizationregistry "^1.1.0" - is-generator-function "^1.0.10" - is-regex "^1.2.1" - is-weakref "^1.0.2" - isarray "^2.0.5" - which-boxed-primitive "^1.1.0" - which-collection "^1.0.2" - which-typed-array "^1.1.16" - -which-collection@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" - integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== - dependencies: - is-map "^2.0.3" - is-set "^2.0.3" - is-weakmap "^2.0.2" - is-weakset "^2.0.3" - -which-module@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" - integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== - -which-typed-array@^1.1.16, which-typed-array@^1.1.19, which-typed-array@^1.1.2: - version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== - 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" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -winston-transport@^4.4.0, winston-transport@^4.5.0, winston-transport@^4.9.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.9.0.tgz#3bba345de10297654ea6f33519424560003b3bf9" - integrity sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A== - dependencies: - logform "^2.7.0" - readable-stream "^3.6.2" - triple-beam "^1.3.0" - -winston@^3: - version "3.18.3" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.18.3.tgz#93ac10808c8e1081d723bc8811cd2f445ddfdcd1" - integrity sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww== - dependencies: - "@colors/colors" "^1.6.0" - "@dabh/diagnostics" "^2.0.8" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.7.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.9.0" - -word-wrap@1.2.5, word-wrap@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -write-file-atomic@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -ws@^8.17.1: - version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== - -xml2js@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" - integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@15.1.1: - version "15.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - -xss@^1.0.8: - version "1.0.15" - resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a" - integrity sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg== - dependencies: - commander "^2.20.3" - cssfilter "0.0.10" - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -"yargs@12 - 15": - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yargs@^17.3.1, yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yarn-audit-fix@^10.1.0: - version "10.1.1" - resolved "https://registry.yarnpkg.com/yarn-audit-fix/-/yarn-audit-fix-10.1.1.tgz#a666742c3d399de6bb82709a7885a796861ef11f" - integrity sha512-QkhTDdbiXKYdN+ilVoCf8f0hsolpZbZF8UIoSnAPxJGe9K/8tSIaoYWnLvtjJ5xHIDU0tlT95IxDCN8BsTWQlw== - dependencies: - "@types/fs-extra" "^11.0.4" - "@types/lodash-es" "^4.17.12" - "@types/semver" "^7.5.8" - "@types/yarnpkg__lockfile" "^1.1.9" - "@yarnpkg/lockfile" "^1.1.0" - chalk "^5.3.0" - commander "^12.1.0" - fast-glob "^3.3.2" - fs-extra "^11.2.0" - js-yaml "^4.1.0" - lodash-es "^4.17.21" - semver "^7.6.3" - synp "^1.9.14" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zen-observable-ts@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" - integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== - dependencies: - zen-observable "0.8.15" - -zen-observable@0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" - integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==