diff --git a/.env.example b/.env.example index f7c086421..b7605583e 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,5 @@ BASE_URL="http://localhost:3000" POSTGRES_URL="postgresql://ctrlplane:ctrlplane@127.0.0.1:5432/ctrlplane" -REDIS_URL="redis://127.0.0.1:6379" KAFKA_BROKERS=localhost:9092 AUTH_SECRET='supersecret' diff --git a/.github/workflows/apps-event-worker.yaml b/.github/workflows/apps-event-worker.yaml deleted file mode 100644 index 427d15ef3..000000000 --- a/.github/workflows/apps-event-worker.yaml +++ /dev/null @@ -1,89 +0,0 @@ -name: Apps / Event Worker - -on: - pull_request: - branches: ["*"] - paths: - - apps/event-worker/** - - packages/db/** - - packages/job-dispatch/** - - packages/validators/** - - packages/secrets/** - - packages/rule-engine/** - - packages/events/** - - .github/workflows/apps-event-worker.yaml - - pnpm-lock.yaml - push: - branches: ["main"] - paths: - - apps/event-worker/** - - packages/db/** - - packages/job-dispatch/** - - packages/validators/** - - packages/secrets/** - - packages/rule-engine/** - - packages/events/** - - .github/workflows/apps-event-worker.yaml - - pnpm-lock.yaml - -jobs: - build: - runs-on: ubuntu-latest - - permissions: - contents: read - id-token: write - - strategy: - matrix: - platform: [linux/amd64] - - steps: - - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Check if Docker Hub secrets are available - run: | - if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then - echo "DOCKERHUB_LOGIN=false" >> $GITHUB_ENV - else - echo "DOCKERHUB_LOGIN=true" >> $GITHUB_ENV - fi - - - name: Login to Docker Hub - uses: docker/login-action@v3 - if: env.DOCKERHUB_LOGIN == 'true' - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ctrlplane/event-worker - tags: | - type=sha,format=short,prefix= - - - name: Build - uses: docker/build-push-action@v6 - if: github.ref != 'refs/heads/main' - with: - push: false - file: apps/event-worker/Dockerfile - platforms: ${{ matrix.platform }} - tags: ${{ steps.meta.outputs.tags }} - - - name: Build and Push - uses: docker/build-push-action@v6 - if: github.ref == 'refs/heads/main' && env.DOCKERHUB_LOGIN == 'true' - with: - push: true - file: apps/event-worker/Dockerfile - platforms: ${{ matrix.platform }} - tags: ${{ steps.meta.outputs.tags }} diff --git a/apps/event-queue/src/instrumentation-node.ts b/apps/event-queue/src/instrumentation-node.ts index 39ce06796..2d7a1fc21 100644 --- a/apps/event-queue/src/instrumentation-node.ts +++ b/apps/event-queue/src/instrumentation-node.ts @@ -35,9 +35,6 @@ const sdk = new NodeSDK({ enhancedDatabaseReporting: true, addSqlCommenterCommentToQueries: true, }, - "@opentelemetry/instrumentation-ioredis": { - enabled: true, - }, "@opentelemetry/instrumentation-winston": { enabled: true, logHook: (span, record) => { diff --git a/apps/event-worker/Dockerfile b/apps/event-worker/Dockerfile deleted file mode 100644 index 7929d91f5..000000000 --- a/apps/event-worker/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# https://github.com/WhiskeySockets/Baileys/issues/1003#issuecomment-2306467419 -ARG NODE_VERSION=20 -FROM node:${NODE_VERSION}-alpine - -RUN apk add --no-cache libc6-compat python3 make g++ curl - -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" - -RUN curl -L -o vcluster "https://github.com/loft-sh/vcluster/releases/latest/download/vcluster-linux-amd64" && \ - chmod +x vcluster && \ - mv vcluster /usr/local/bin - -RUN npm install -g turbo -RUN npm install -g corepack@latest -RUN corepack enable pnpm - -WORKDIR /app - -COPY .gitignore .gitignore -COPY turbo.json turbo.json -RUN pnpm add -g turbo - -COPY package.json package.json -COPY .npmrc .npmrc -COPY pnpm-*.yaml . - -COPY tooling/prettier/package.json ./tooling/prettier/package.json -COPY tooling/eslint/package.json ./tooling/eslint/package.json -COPY tooling/typescript/package.json ./tooling/typescript/package.json - -COPY packages/db/package.json ./packages/db/package.json -COPY packages/validators/package.json ./packages/validators/package.json -COPY packages/logger/package.json ./packages/logger/package.json -COPY packages/job-dispatch/package.json ./packages/job-dispatch/package.json -COPY packages/rule-engine/package.json ./packages/rule-engine/package.json -COPY packages/secrets/package.json ./packages/secrets/package.json -COPY packages/events/package.json ./packages/events/package.json -COPY packages/workspace-engine-sdk/package.json ./packages/workspace-engine-sdk/package.json - -COPY apps/event-worker/package.json ./apps/event-worker/package.json - -RUN pnpm install --frozen-lockfile - -COPY . . - -RUN turbo build --filter=...@ctrlplane/event-worker - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nodejs -USER nodejs - -ENV NODE_ENV=production -ENV NODE_OPTIONS="--max-old-space-size=4096" - -EXPOSE 3123 - -CMD node -r ./apps/event-worker/dist/instrumentation-node.js --max-old-space-size=${NODE_MAX_OLD_SPACE_SIZE} apps/event-worker/dist/index.js diff --git a/apps/event-worker/README.md b/apps/event-worker/README.md deleted file mode 100644 index 42eb24807..000000000 --- a/apps/event-worker/README.md +++ /dev/null @@ -1 +0,0 @@ -# Event Worker diff --git a/apps/event-worker/eslint.config.js b/apps/event-worker/eslint.config.js deleted file mode 100644 index 48eb41bc9..000000000 --- a/apps/event-worker/eslint.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import baseConfig, { requireJsSuffix } from "@ctrlplane/eslint-config/base"; - -/** @type {import('typescript-eslint').Config} */ -export default [ - { - ignores: [".nitro/**", ".output/**", "dist/**"], - }, - ...requireJsSuffix, - ...baseConfig, -]; diff --git a/apps/event-worker/package.json b/apps/event-worker/package.json deleted file mode 100644 index f2818c875..000000000 --- a/apps/event-worker/package.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "@ctrlplane/event-worker", - "private": true, - "type": "module", - "scripts": { - "clean": "rm -rf .turbo node_modules", - "dev": "pnpm with-env tsx watch --clear-screen=false src/index.ts", - "lint": "eslint", - "build": "tsc", - "format": "prettier --check . --ignore-path ../../.gitignore", - "typecheck": "tsc --noEmit", - "with-env": "dotenv -e ../../.env --" - }, - "dependencies": { - "@aws-sdk/client-ec2": "^3.701.0", - "@aws-sdk/client-eks": "^3.699.0", - "@aws-sdk/client-sts": "^3.699.0", - "@azure/arm-containerservice": "^21.3.0", - "@azure/identity": "^4.5.0", - "@ctrlplane/db": "workspace:*", - "@ctrlplane/events": "workspace:*", - "@ctrlplane/job-dispatch": "workspace:*", - "@ctrlplane/logger": "workspace:*", - "@ctrlplane/rule-engine": "workspace:*", - "@ctrlplane/validators": "workspace:*", - "@google-cloud/compute": "^4.9.0", - "@google-cloud/container": "^5.16.0", - "@kubernetes/client-node": "^0.22.0", - "@octokit/auth-app": "catalog:", - "@octokit/rest": "catalog:", - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/auto-instrumentations-node": "^0.52.1", - "@opentelemetry/exporter-logs-otlp-http": "^0.54.2", - "@opentelemetry/exporter-trace-otlp-http": "^0.54.2", - "@opentelemetry/resources": "^1.27.0", - "@opentelemetry/sdk-logs": "^0.54.2", - "@opentelemetry/sdk-node": "^0.54.2", - "@opentelemetry/sdk-trace-base": "^1.27.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@smithy/types": "^3.7.1", - "@t3-oss/env-core": "catalog:", - "bullmq": "catalog:", - "cron": "^3.1.7", - "dotenv": "^16.4.5", - "google-auth-library": "^9.13.0", - "ioredis": "catalog:", - "js-yaml": "^4.1.0", - "lodash": "catalog:", - "ms": "catalog:", - "redis-semaphore": "catalog:", - "semver": "catalog:", - "ts-is-present": "^1.2.2", - "uuid": "^10.0.0", - "zod": "catalog:" - }, - "devDependencies": { - "@ctrlplane/eslint-config": "workspace:^", - "@ctrlplane/prettier-config": "workspace:^", - "@ctrlplane/tsconfig": "workspace:*", - "@types/js-yaml": "^4.0.9", - "@types/lodash": "catalog:", - "@types/ms": "catalog:", - "@types/node": "catalog:node22", - "@types/semver": "^7.5.8", - "@types/uuid": "^10.0.0", - "eslint": "catalog:", - "prettier": "catalog:", - "tsx": "catalog:", - "typescript": "catalog:" - }, - "prettier": "@ctrlplane/prettier-config" -} diff --git a/apps/event-worker/src/config.ts b/apps/event-worker/src/config.ts deleted file mode 100644 index 601e531c3..000000000 --- a/apps/event-worker/src/config.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createEnv } from "@t3-oss/env-core"; -import dotenv from "dotenv"; -import { z } from "zod"; - -dotenv.config(); - -export const env = createEnv({ - server: { - NODE_ENV: z - .enum(["development", "production", "test"]) - .default("development"), - POSTGRES_URL: z.string().url(), - REDIS_URL: z.string().url(), - GITHUB_BOT_APP_ID: z.string().optional(), - GITHUB_BOT_PRIVATE_KEY: z.string().optional(), - GITHUB_BOT_CLIENT_ID: z.string().optional(), - GITHUB_BOT_CLIENT_SECRET: z.string().optional(), - AZURE_APP_CLIENT_ID: z.string().optional(), - AZURE_APP_CLIENT_SECRET: z.string().optional(), - PORT: z.number().default(3123), - }, - runtimeEnv: process.env, -}); diff --git a/apps/event-worker/src/github-utils.ts b/apps/event-worker/src/github-utils.ts deleted file mode 100644 index 993734739..000000000 --- a/apps/event-worker/src/github-utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createAppAuth } from "@octokit/auth-app"; -import { Octokit } from "@octokit/rest"; - -import { env } from "./config.js"; - -export const getInstallationOctokit = (installationId: number) => - env.GITHUB_BOT_APP_ID && - env.GITHUB_BOT_PRIVATE_KEY && - env.GITHUB_BOT_CLIENT_ID && - env.GITHUB_BOT_CLIENT_SECRET - ? new Octokit({ - authStrategy: createAppAuth, - auth: { - appId: env.GITHUB_BOT_APP_ID, - privateKey: env.GITHUB_BOT_PRIVATE_KEY, - clientId: env.GITHUB_BOT_CLIENT_ID, - clientSecret: env.GITHUB_BOT_CLIENT_SECRET, - installationId, - }, - }) - : null; diff --git a/apps/event-worker/src/index.ts b/apps/event-worker/src/index.ts deleted file mode 100644 index abd05ed1b..000000000 --- a/apps/event-worker/src/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createServer } from "node:http"; - -import { logger } from "@ctrlplane/logger"; - -import { env } from "./config.js"; -import { workers } from "./workers/index.js"; - -console.log("Registering instrumentation..."); - -const shutdown = () => { - logger.warn("Exiting..."); - Promise.all(Object.values(workers).map((w) => w?.close())).then(() => - process.exit(0), - ); -}; - -process.on("SIGTERM", shutdown); -process.on("SIGINT", shutdown); - -const server = createServer((req, res) => { - if (req.url === "/healthz") { - res.writeHead(200); - res.end("ok"); - return; - } - - res.writeHead(404); - res.end(); -}); - -const port = env.PORT; -server.listen(port, () => { - logger.info(`Health check endpoint listening on port ${port}`); -}); diff --git a/apps/event-worker/src/instrumentation-node.ts b/apps/event-worker/src/instrumentation-node.ts deleted file mode 100644 index 3e222aa02..000000000 --- a/apps/event-worker/src/instrumentation-node.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"; -import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; -import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; -import { Resource } from "@opentelemetry/resources"; -import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs"; -import { NodeSDK } from "@opentelemetry/sdk-node"; -import { - AlwaysOnSampler, - BatchSpanProcessor, -} from "@opentelemetry/sdk-trace-base"; -import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions"; - -const sdk = new NodeSDK({ - resource: new Resource({ - [ATTR_SERVICE_NAME]: "ctrlplane/event-worker", - }), - spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter()) as any], - logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter())], - instrumentations: [ - getNodeAutoInstrumentations({ - "@opentelemetry/instrumentation-fs": { - enabled: false, - }, - "@opentelemetry/instrumentation-net": { - enabled: false, - }, - "@opentelemetry/instrumentation-dns": { - enabled: false, - }, - "@opentelemetry/instrumentation-http": { - enabled: true, - }, - "@opentelemetry/instrumentation-pg": { - enabled: true, - enhancedDatabaseReporting: true, - addSqlCommenterCommentToQueries: true, - }, - "@opentelemetry/instrumentation-ioredis": { - enabled: true, - }, - "@opentelemetry/instrumentation-winston": { - enabled: true, - logHook: (span, record) => { - record["resource.service.name"] = "ctrlplane/event-worker"; - }, - }, - }), - ], - sampler: new AlwaysOnSampler(), -}); - -try { - sdk.start(); - console.log("Tracing initialized"); -} catch (error) { - console.error("Error initializing tracing", error); -} diff --git a/apps/event-worker/src/instrumentation.ts b/apps/event-worker/src/instrumentation.ts deleted file mode 100644 index 3240e018a..000000000 --- a/apps/event-worker/src/instrumentation.ts +++ /dev/null @@ -1,4 +0,0 @@ -export async function register() { - if (process.env.NODE_ENV === "production") - await import("./instrumentation-node.js"); -} diff --git a/apps/event-worker/src/redis.ts b/apps/event-worker/src/redis.ts deleted file mode 100644 index ad8c866df..000000000 --- a/apps/event-worker/src/redis.ts +++ /dev/null @@ -1,22 +0,0 @@ -import IORedis from "ioredis"; - -import { logger } from "@ctrlplane/logger"; - -import { env } from "./config.js"; - -const log = logger.child({ - module: "redis", -}); - -export const redis = new IORedis(env.REDIS_URL, { - maxRetriesPerRequest: null, -}); - -redis.on("connect", () => { - log.info("Redis connected"); -}); - -redis.on("error", (err) => { - log.error("Redis error", err); - console.error("Redis error", err); -}); diff --git a/apps/event-worker/src/utils/omit-null-undefined.ts b/apps/event-worker/src/utils/omit-null-undefined.ts deleted file mode 100644 index dc99eda3d..000000000 --- a/apps/event-worker/src/utils/omit-null-undefined.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function omitNullUndefined(obj: object) { - return Object.entries(obj).reduce>( - (acc, [key, value]) => { - if (value !== null && value !== undefined) acc[key] = value; - return acc; - }, - {}, - ); -} diff --git a/apps/event-worker/src/workers/compute-deployment-resource-selector.ts b/apps/event-worker/src/workers/compute-deployment-resource-selector.ts deleted file mode 100644 index 16d2a4a88..000000000 --- a/apps/event-worker/src/workers/compute-deployment-resource-selector.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { and, eq, isNull, selector, sql } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -export const computeDeploymentResourceSelectorWorkerEvent = createWorker( - Channel.ComputeDeploymentResourceSelector, - async (job) => { - const { id } = job.data; - - const deployment = await db.query.deployment.findFirst({ - where: eq(schema.deployment.id, id), - with: { system: true }, - }); - - if (deployment == null) throw new Error("Deployment not found"); - - const { workspaceId } = deployment.system; - try { - await db.transaction(async (tx) => { - await tx.execute( - sql` - SELECT * from ${schema.computedDeploymentResource} - WHERE ${eq(schema.computedDeploymentResource.deploymentId, deployment.id)} - FOR UPDATE NOWAIT - `, - ); - - await tx - .delete(schema.computedDeploymentResource) - .where( - eq(schema.computedDeploymentResource.deploymentId, deployment.id), - ); - - if (deployment.resourceSelector == null) return; - - const resources = await tx.query.resource.findMany({ - where: and( - eq(schema.resource.workspaceId, workspaceId), - selector() - .query() - .resources() - .where(deployment.resourceSelector) - .sql(), - isNull(schema.resource.deletedAt), - ), - }); - - const computedDeploymentResources = resources.map((r) => ({ - deploymentId: deployment.id, - resourceId: r.id, - })); - - if (computedDeploymentResources.length > 0) - await tx - .insert(schema.computedDeploymentResource) - .values(computedDeploymentResources) - .onConflictDoNothing(); - }); - - dispatchQueueJob().toCompute().system(deployment.system).releaseTargets(); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - if (isRowLocked) { - dispatchQueueJob() - .toCompute() - .deployment(deployment) - .resourceSelector(); - return; - } - - throw e; - } - }, -); diff --git a/apps/event-worker/src/workers/compute-environment-resource-selector.ts b/apps/event-worker/src/workers/compute-environment-resource-selector.ts deleted file mode 100644 index e5853aebd..000000000 --- a/apps/event-worker/src/workers/compute-environment-resource-selector.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { and, eq, isNull, selector, sql } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -/** - * Worker that computes and updates the resources associated with an environment - * based on its resource selector. - * - * When an environment's resource selector changes, we need to recompute which - * resources match the selector and update the computed_environment_resource - * table accordingly. This ensures that the environment's resource associations - * stay in sync with the selector criteria. - * - * The worker: - * 1. Acquires a db lock to prevent concurrent updates to the same - * environment - * 2. Deletes existing computed resource associations - * 3. Finds all resources matching the environment's selector - * 4. Inserts new computed resource associations - * 5. If lock acquisition fails, retries after 500ms - */ -export const computeEnvironmentResourceSelectorWorkerEvent = createWorker( - Channel.ComputeEnvironmentResourceSelector, - async (job) => { - const { id } = job.data; - - const environment = await db.query.environment.findFirst({ - where: eq(schema.environment.id, id), - with: { system: true }, - }); - - if (environment == null) throw new Error("Environment not found"); - - const { workspaceId } = environment.system; - try { - await db.transaction(async (tx) => { - // acquire a lock on the environment - await tx.execute( - sql` - SELECT * from ${schema.computedEnvironmentResource} - WHERE ${eq(schema.computedEnvironmentResource.environmentId, environment.id)} - FOR UPDATE NOWAIT - `, - ); - - await tx - .delete(schema.computedEnvironmentResource) - .where( - eq( - schema.computedEnvironmentResource.environmentId, - environment.id, - ), - ); - - if (environment.resourceSelector == null) return; - - const resources = await tx.query.resource.findMany({ - where: and( - eq(schema.resource.workspaceId, workspaceId), - selector() - .query() - .resources() - .where(environment.resourceSelector) - .sql(), - isNull(schema.resource.deletedAt), - ), - }); - - const computedEnvironmentResources = resources.map((r) => ({ - environmentId: environment.id, - resourceId: r.id, - })); - - if (computedEnvironmentResources.length === 0) return; - await tx - .insert(schema.computedEnvironmentResource) - .values(computedEnvironmentResources) - .onConflictDoNothing(); - }); - dispatchQueueJob() - .toCompute() - .system(environment.system) - .releaseTargets(); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - if (isRowLocked) { - dispatchQueueJob() - .toCompute() - .environment(environment) - .resourceSelector(); - return; - } - - throw e; - } - }, -); diff --git a/apps/event-worker/src/workers/compute-policy-target-release-target-selector.ts b/apps/event-worker/src/workers/compute-policy-target-release-target-selector.ts deleted file mode 100644 index 1ce095775..000000000 --- a/apps/event-worker/src/workers/compute-policy-target-release-target-selector.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { and, eq, inArray, isNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import { computePolicyTargets } from "@ctrlplane/db/queries"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -export const computePolicyTargetReleaseTargetSelectorWorkerEvent = createWorker( - Channel.ComputePolicyTargetReleaseTargetSelector, - async (job) => { - const { id } = job.data; - - const policyTarget = await db.query.policyTarget.findFirst({ - where: eq(schema.policyTarget.id, id), - }); - - if (policyTarget == null) throw new Error("Policy target not found"); - - try { - const changedReleaseTaretIds = await computePolicyTargets( - db, - policyTarget, - ); - - const releaseTargets = await db - .select() - .from(schema.releaseTarget) - .innerJoin( - schema.resource, - eq(schema.releaseTarget.resourceId, schema.resource.id), - ) - .where( - and( - inArray(schema.releaseTarget.id, changedReleaseTaretIds), - isNull(schema.resource.deletedAt), - ), - ) - .then((rows) => rows.map((row) => row.release_target)); - - dispatchQueueJob().toEvaluate().releaseTargets(releaseTargets); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - if (isRowLocked) { - dispatchQueueJob() - .toCompute() - .policyTarget(policyTarget) - .releaseTargetSelector(); - return; - } - - throw e; - } - }, -); diff --git a/apps/event-worker/src/workers/compute-systems-release-targets.ts b/apps/event-worker/src/workers/compute-systems-release-targets.ts deleted file mode 100644 index bcd3cca39..000000000 --- a/apps/event-worker/src/workers/compute-systems-release-targets.ts +++ /dev/null @@ -1,206 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { and, eq, inArray, isNull, or, sql } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { - Channel, - createWorker, - dispatchQueueJob, - getQueue, -} from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; - -const log = logger.child({ component: "computeSystemsReleaseTargetsWorker" }); - -const findMatchingEnvironmentDeploymentPairs = ( - tx: Tx, - system: { id: string; workspaceId: string }, -) => { - const { id: systemId, workspaceId } = system; - - const isResourceMatchingEnvironment = eq( - schema.computedEnvironmentResource.resourceId, - schema.resource.id, - ); - const isResourceMatchingDeployment = or( - isNull(schema.deployment.resourceSelector), - eq(schema.computedDeploymentResource.resourceId, schema.resource.id), - ); - - return tx - .select({ - environmentId: schema.environment.id, - deploymentId: schema.deployment.id, - resourceId: schema.resource.id, - }) - .from(schema.resource) - .innerJoin( - schema.computedEnvironmentResource, - eq(schema.computedEnvironmentResource.resourceId, schema.resource.id), - ) - .innerJoin( - schema.environment, - eq( - schema.computedEnvironmentResource.environmentId, - schema.environment.id, - ), - ) - .innerJoin( - schema.deployment, - eq(schema.deployment.systemId, schema.environment.systemId), - ) - .leftJoin( - schema.computedDeploymentResource, - eq(schema.computedDeploymentResource.deploymentId, schema.deployment.id), - ) - .where( - and( - isResourceMatchingEnvironment, - isResourceMatchingDeployment, - eq(schema.environment.systemId, systemId), - eq(schema.deployment.systemId, systemId), - eq(schema.resource.workspaceId, workspaceId), - isNull(schema.resource.deletedAt), - ), - ); -}; - -export const computeSystemsReleaseTargetsWorker = createWorker( - Channel.ComputeSystemsReleaseTargets, - async (job) => { - const { id: systemId } = job.data; - - const system = await db.query.system.findFirst({ - where: eq(schema.system.id, systemId), - with: { deployments: true, environments: true }, - }); - - if (system == null) throw new Error("System not found"); - - const { deployments, environments } = system; - const deploymentIds = deployments.map((d) => d.id); - const environmentIds = environments.map((e) => e.id); - const { workspaceId } = system; - - if (deploymentIds.length === 0 || environmentIds.length === 0) return; - - try { - const { created, deleted } = await db.transaction(async (tx) => { - await tx.execute( - sql` - SELECT ${schema.releaseTarget.id} FROM ${schema.releaseTarget} - WHERE ${or( - inArray(schema.releaseTarget.deploymentId, deploymentIds), - inArray(schema.releaseTarget.environmentId, environmentIds), - )} - FOR UPDATE NOWAIT - `, - ); - - await tx.execute( - sql` - SELECT * FROM ${schema.computedEnvironmentResource} - WHERE ${inArray(schema.computedEnvironmentResource.environmentId, environmentIds)} - FOR UPDATE NOWAIT - `, - ); - - await tx.execute( - sql` - SELECT * FROM ${schema.computedDeploymentResource} - WHERE ${inArray(schema.computedDeploymentResource.deploymentId, deploymentIds)} - FOR UPDATE NOWAIT - `, - ); - - const previousReleaseTargets = await tx.query.releaseTarget.findMany({ - where: or( - inArray(schema.releaseTarget.deploymentId, deploymentIds), - inArray(schema.releaseTarget.environmentId, environmentIds), - ), - }); - - const releaseTargets = await findMatchingEnvironmentDeploymentPairs( - tx, - system, - ); - - const deleted = previousReleaseTargets.filter( - (prevRt) => - !releaseTargets.some( - (rt) => - rt.deploymentId === prevRt.deploymentId && - rt.resourceId === prevRt.resourceId && - rt.environmentId === prevRt.environmentId, - ), - ); - - if (deleted.length > 0) - await tx.delete(schema.releaseTarget).where( - inArray( - schema.releaseTarget.id, - deleted.map((rt) => rt.id), - ), - ); - - const created = releaseTargets.filter( - (rt) => - !previousReleaseTargets.some( - (prevRt) => - prevRt.deploymentId === rt.deploymentId && - prevRt.resourceId === rt.resourceId && - prevRt.environmentId === rt.environmentId, - ), - ); - - const inserted = - created.length > 0 - ? await tx - .insert(schema.releaseTarget) - .values(created) - .onConflictDoNothing() - .returning() - : []; - - return { created: inserted, deleted }; - }); - - if (deleted.length > 0) - for (const rt of deleted) - getQueue(Channel.DeletedReleaseTarget).add(rt.id, rt, { - deduplication: { id: rt.id }, - }); - - if (created.length === 0) return; - - const policyTargets = await db - .select() - .from(schema.policyTarget) - .innerJoin( - schema.policy, - eq(schema.policyTarget.policyId, schema.policy.id), - ) - .where(and(eq(schema.policy.workspaceId, workspaceId))); - - if (policyTargets.length === 0) { - await dispatchQueueJob().toEvaluate().releaseTargets(created); - return; - } - - await dispatchQueueJob() - .toCompute() - .workspace(workspaceId) - .policyTargets({ releaseTargetsToEvaluate: created }); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - if (isRowLocked) { - dispatchQueueJob().toCompute().system(system).releaseTargets(); - return; - } - log.error("Failed to compute release targets", { error: e }); - - throw e; - } - }, -); diff --git a/apps/event-worker/src/workers/compute-workspace-policy-targets.ts b/apps/event-worker/src/workers/compute-workspace-policy-targets.ts deleted file mode 100644 index deb3a76c2..000000000 --- a/apps/event-worker/src/workers/compute-workspace-policy-targets.ts +++ /dev/null @@ -1,167 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { and, eq, inArray, isNull, selector, sql } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; - -const log = logger.child({ module: "compute-workspace-policy-targets" }); - -const getPolicyTargets = async (tx: Tx, workspaceId: string) => - tx - .select() - .from(schema.policyTarget) - .innerJoin( - schema.policy, - eq(schema.policyTarget.policyId, schema.policy.id), - ) - .where(eq(schema.policy.workspaceId, workspaceId)); - -const findMatchingReleaseTargets = ( - tx: Tx, - policyTarget: schema.PolicyTarget, -) => - tx - .select() - .from(schema.releaseTarget) - .innerJoin( - schema.resource, - eq(schema.releaseTarget.resourceId, schema.resource.id), - ) - .innerJoin( - schema.deployment, - eq(schema.releaseTarget.deploymentId, schema.deployment.id), - ) - .innerJoin( - schema.environment, - eq(schema.releaseTarget.environmentId, schema.environment.id), - ) - .where( - and( - isNull(schema.resource.deletedAt), - selector() - .query() - .resources() - .where(policyTarget.resourceSelector) - .sql(), - selector() - .query() - .deployments() - .where(policyTarget.deploymentSelector) - .sql(), - selector() - .query() - .environments() - .where(policyTarget.environmentSelector) - .sql(), - ), - ) - .then((rt) => - rt.map((rt) => ({ - policyTargetId: policyTarget.id, - releaseTargetId: rt.release_target.id, - })), - ); - -const acquireWorkspacePolicyTargetsLock = async (tx: Tx, workspaceId: string) => - tx.execute( - sql` - SELECT * FROM ${schema.policyTarget} - INNER JOIN ${schema.policy} ON ${eq(schema.policyTarget.policyId, schema.policy.id)} - WHERE ${eq(schema.policy.workspaceId, workspaceId)} - FOR UPDATE NOWAIT - `, - ); - -const maybeAcquireReleaseTargetsLock = async ( - tx: Tx, - releaseTargets?: schema.ReleaseTarget[], -) => { - const releaseTargetsIds = releaseTargets?.map((rt) => rt.id) ?? []; - if (releaseTargetsIds.length === 0) return; - - await tx.execute( - sql` - SELECT * FROM ${schema.releaseTarget} - WHERE ${inArray(schema.releaseTarget.id, releaseTargetsIds)} - FOR UPDATE NOWAIT - `, - ); -}; - -const computePolicyTarget = async ( - tx: Tx, - policyTarget: schema.PolicyTarget, -) => { - const previous = await tx - .select() - .from(schema.computedPolicyTargetReleaseTarget) - .where( - eq( - schema.computedPolicyTargetReleaseTarget.policyTargetId, - policyTarget.id, - ), - ); - - const releaseTargets = await findMatchingReleaseTargets(tx, policyTarget); - - const prevIds = new Set(previous.map((rt) => rt.releaseTargetId)); - const nextIds = new Set(releaseTargets.map((rt) => rt.releaseTargetId)); - const deleted = previous.filter((rt) => !nextIds.has(rt.releaseTargetId)); - const created = releaseTargets.filter( - (rt) => !prevIds.has(rt.releaseTargetId), - ); - - if (deleted.length > 0) - await tx.delete(schema.computedPolicyTargetReleaseTarget).where( - inArray( - schema.computedPolicyTargetReleaseTarget.releaseTargetId, - deleted.map((rt) => rt.releaseTargetId), - ), - ); - - if (created.length > 0) - await tx - .insert(schema.computedPolicyTargetReleaseTarget) - .values(created) - .onConflictDoNothing(); - - return [...created, ...deleted].map((rt) => rt.releaseTargetId); -}; - -export const computeWorkspacePolicyTargetsWorker = createWorker( - Channel.ComputeWorkspacePolicyTargets, - async (job) => { - const { workspaceId, releaseTargetsToEvaluate } = job.data; - - try { - await db.transaction(async (tx) => { - await maybeAcquireReleaseTargetsLock(tx, releaseTargetsToEvaluate); - await acquireWorkspacePolicyTargetsLock(tx, workspaceId); - - const policyTargets = await getPolicyTargets(tx, workspaceId); - if (policyTargets.length === 0) return; - - for (const { policy_target: policyTarget } of policyTargets) - await computePolicyTarget(tx, policyTarget); - }); - } catch (e: any) { - const isRowLockError = e.code === "55P03"; - if (isRowLockError) { - dispatchQueueJob().toCompute().workspace(workspaceId).policyTargets({ - releaseTargetsToEvaluate, - }); - return; - } - - log.error("Failed to compute workspace policy targets", { error: e }); - throw e; - } - - if (releaseTargetsToEvaluate != null) - await dispatchQueueJob() - .toEvaluate() - .releaseTargets(releaseTargetsToEvaluate); - }, -); diff --git a/apps/event-worker/src/workers/delete-deployment.ts b/apps/event-worker/src/workers/delete-deployment.ts deleted file mode 100644 index 1fa58dddb..000000000 --- a/apps/event-worker/src/workers/delete-deployment.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { eq, sql } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, getQueue } from "@ctrlplane/events"; - -export const deleteDeploymentWorker = createWorker( - Channel.DeleteDeployment, - async (job) => { - const { id: deploymentId } = job.data; - - try { - const releaseTargets = await db.transaction(async (tx) => { - await tx.execute( - sql` - SELECT * from ${schema.deployment} - INNER JOIN ${schema.releaseTarget} ON ${eq(schema.releaseTarget.deploymentId, schema.deployment.id)} - WHERE ${eq(schema.deployment.id, deploymentId)} - FOR UPDATE NOWAIT - `, - ); - - const releaseTargets = await tx.query.releaseTarget.findMany({ - where: eq(schema.releaseTarget.deploymentId, deploymentId), - }); - - await tx - .delete(schema.deployment) - .where(eq(schema.deployment.id, deploymentId)); - - return releaseTargets; - }); - - for (const rt of releaseTargets) - getQueue(Channel.DeletedReleaseTarget).add(rt.id, rt, { - deduplication: { id: rt.id }, - }); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - if (isRowLocked) { - await getQueue(Channel.DeleteDeployment).add(job.name, job.data); - return; - } - - throw e; - } - }, -); diff --git a/apps/event-worker/src/workers/delete-environment.ts b/apps/event-worker/src/workers/delete-environment.ts deleted file mode 100644 index c269cbf87..000000000 --- a/apps/event-worker/src/workers/delete-environment.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { eq, sql } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, getQueue } from "@ctrlplane/events"; - -export const deleteEnvironmentWorker = createWorker( - Channel.DeleteEnvironment, - async (job) => { - const { id: environmentId } = job.data; - - try { - const releaseTargets = await db.transaction(async (tx) => { - await tx.execute( - sql` - SELECT * from ${schema.environment} - INNER JOIN ${schema.releaseTarget} ON ${eq(schema.releaseTarget.environmentId, schema.environment.id)} - WHERE ${eq(schema.environment.id, environmentId)} - FOR UPDATE NOWAIT - `, - ); - - const releaseTargets = await tx.query.releaseTarget.findMany({ - where: eq(schema.releaseTarget.environmentId, environmentId), - }); - - await tx - .delete(schema.environment) - .where(eq(schema.environment.id, environmentId)); - - return releaseTargets; - }); - - for (const rt of releaseTargets) - getQueue(Channel.DeletedReleaseTarget).add(rt.id, rt, { - deduplication: { id: rt.id }, - }); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - if (isRowLocked) { - await getQueue(Channel.DeleteEnvironment).add(job.name, job.data); - return; - } - - throw e; - } - }, -); diff --git a/apps/event-worker/src/workers/delete-resource.ts b/apps/event-worker/src/workers/delete-resource.ts deleted file mode 100644 index fce1f94e7..000000000 --- a/apps/event-worker/src/workers/delete-resource.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; -import _ from "lodash"; - -import { eq, inArray } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import { getResourceChildren } from "@ctrlplane/db/queries"; -import * as SCHEMA from "@ctrlplane/db/schema"; -import { - Channel, - createWorker, - dispatchQueueJob, - getQueue, -} from "@ctrlplane/events"; - -const softDeleteResource = async (tx: Tx, resource: SCHEMA.Resource) => - tx - .update(SCHEMA.resource) - .set({ deletedAt: new Date() }) - .where(eq(SCHEMA.resource.id, resource.id)); - -const deleteReleaseTargets = async (tx: Tx, resource: SCHEMA.Resource) => - tx - .delete(SCHEMA.releaseTarget) - .where(eq(SCHEMA.releaseTarget.resourceId, resource.id)) - .returning(); - -const deleteComputedResources = async (tx: Tx, resource: SCHEMA.Resource) => - Promise.all([ - tx - .delete(SCHEMA.computedDeploymentResource) - .where(eq(SCHEMA.computedDeploymentResource.resourceId, resource.id)), - tx - .delete(SCHEMA.computedEnvironmentResource) - .where(eq(SCHEMA.computedEnvironmentResource.resourceId, resource.id)), - ]); - -const dispatchAffectedTargetJobs = async ( - tx: Tx, - resource: SCHEMA.Resource, -) => { - const affectedResources = await getResourceChildren(tx, resource.id); - - const affectedReleaseTargets = await db - .selectDistinctOn([SCHEMA.releaseTarget.id]) - .from(SCHEMA.releaseTarget) - .where( - inArray( - SCHEMA.releaseTarget.resourceId, - affectedResources.map((r) => r.target.id), - ), - ); - - await dispatchQueueJob().toEvaluate().releaseTargets(affectedReleaseTargets); -}; - -export const deleteResourceWorker = createWorker( - Channel.DeleteResource, - async ({ data: resource }) => { - await db.transaction(async (tx) => { - await softDeleteResource(tx, resource); - await deleteComputedResources(tx, resource); - const rts = await deleteReleaseTargets(tx, resource); - for (const rt of rts) - getQueue(Channel.DeletedReleaseTarget).add(rt.id, rt, { - deduplication: { id: rt.id }, - }); - }); - - await dispatchAffectedTargetJobs(db, resource); - }, -); diff --git a/apps/event-worker/src/workers/deleted-release-target.ts b/apps/event-worker/src/workers/deleted-release-target.ts deleted file mode 100644 index 9b98119a0..000000000 --- a/apps/event-worker/src/workers/deleted-release-target.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { eq, takeFirst, takeFirstOrNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker } from "@ctrlplane/events"; -import { handleEvent } from "@ctrlplane/job-dispatch"; -import { logger } from "@ctrlplane/logger"; -import { HookAction } from "@ctrlplane/validators/events"; - -const log = logger.child({ worker: "deleted-release-target" }); - -/** - * This worker is used to handle post-processing of a deleted release target - * NOTE that the release target has already been deleted by the time this worker - * is called. - */ -export const deletedReleaseTargetWorker = createWorker( - Channel.DeletedReleaseTarget, - async (job) => { - try { - const { data: releaseTarget } = job; - const { deploymentId, resourceId } = releaseTarget; - - const [deployment, resource] = await Promise.all([ - db - .select() - .from(schema.deployment) - .where(eq(schema.deployment.id, deploymentId)) - .then(takeFirstOrNull), - db - .select() - .from(schema.resource) - .where(eq(schema.resource.id, resourceId)) - .then(takeFirst), - ]); - - if (deployment == null) { - log.warn( - "Deployment not found, skipping creating deployment.resource.removed event", - { deploymentId }, - ); - return; - } - - const event = { - action: HookAction.DeploymentResourceRemoved, - payload: { deployment, resource }, - }; - - await handleEvent(event); - } catch (error) { - log.error("Error processing deleted release target", error); - } - }, -); diff --git a/apps/event-worker/src/workers/evaluate-release-target.ts b/apps/event-worker/src/workers/evaluate-release-target.ts deleted file mode 100644 index 56ecdca8b..000000000 --- a/apps/event-worker/src/workers/evaluate-release-target.ts +++ /dev/null @@ -1,260 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; -import type { VersionEvaluateOptions } from "@ctrlplane/rule-engine"; -import _ from "lodash"; - -import { and, desc, eq, sql, takeFirst, takeFirstOrNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import { createReleaseJob } from "@ctrlplane/db/queries"; -import * as schema from "@ctrlplane/db/schema"; -import { - Channel, - createWorker, - dispatchQueueJob, - getQueue, -} from "@ctrlplane/events"; -import { logger, makeWithSpan, trace } from "@ctrlplane/logger"; -import { - VariableReleaseManager, - VersionReleaseManager, -} from "@ctrlplane/rule-engine"; - -const tracer = trace.getTracer("evaluate-release-target"); -const { createSpanWrapper: withSpan } = makeWithSpan(tracer); -const log = logger.child({ module: "evaluate-release-target" }); - -const getReleaseTarget = async ( - tx: Tx, - identifier: { - resourceId: string; - environmentId: string; - deploymentId: string; - }, -) => { - const releaseTarget = await tx.query.releaseTarget.findFirst({ - where: and( - eq(schema.releaseTarget.resourceId, identifier.resourceId), - eq(schema.releaseTarget.environmentId, identifier.environmentId), - eq(schema.releaseTarget.deploymentId, identifier.deploymentId), - ), - with: { - resource: true, - environment: true, - deployment: true, - }, - }); - - if (releaseTarget == null) - throw new Error( - `Release target not found: resourceId=${identifier.resourceId} environmentId=${identifier.environmentId} deploymentId=${identifier.deploymentId}`, - ); - - return releaseTarget; -}; - -/** - * Handles version release evaluation and creation for a release target - * @param releaseTarget - Release target to evaluate - * @returns Created version release - * @throws Error if no candidate is chosen - */ -const handleVersionRelease = withSpan( - "handleVersionRelease", - async ( - span, - tx: Tx, - releaseTarget: any, - versionEvaluateOptions?: VersionEvaluateOptions, - ) => { - const workspaceId = releaseTarget.resource.workspaceId; - - span.setAttribute("releaseTarget.id", String(releaseTarget.id)); - span.setAttribute("workspace.id", workspaceId); - - const vrm = new VersionReleaseManager(tx, { - ...releaseTarget, - workspaceId, - }); - const { chosenCandidate } = await vrm.evaluate(versionEvaluateOptions); - if (!chosenCandidate) return null; - - const { release: versionRelease } = await vrm.upsertRelease( - chosenCandidate.id, - ); - - return versionRelease; - }, -); -/** - * Handles variable release evaluation and creation for a release target - * @param releaseTarget - Release target to evaluate - * @returns Created variable release - */ -const handleVariableRelease = withSpan( - "handleVariableRelease", - async (span, tx: Tx, releaseTarget: any) => { - const workspaceId = releaseTarget.resource.workspaceId; - - span.setAttribute("releaseTarget.id", String(releaseTarget.id)); - span.setAttribute("workspace.id", workspaceId); - - const varrm = new VariableReleaseManager(tx, { - ...releaseTarget, - workspaceId, - }); - - const { chosenCandidate: variableValues } = await varrm.evaluate(); - - const { release: variableRelease } = - await varrm.upsertRelease(variableValues); - - return variableRelease; - }, -); - -const acquireReleaseTargetLock = async (tx: Tx, releaseTargetId: string) => - tx.execute( - sql` - SELECT * FROM ${schema.releaseTarget} - INNER JOIN ${schema.computedPolicyTargetReleaseTarget} ON ${eq(schema.computedPolicyTargetReleaseTarget.releaseTargetId, schema.releaseTarget.id)} - INNER JOIN ${schema.policyTarget} ON ${eq(schema.computedPolicyTargetReleaseTarget.policyTargetId, schema.policyTarget.id)} - WHERE ${eq(schema.releaseTarget.id, releaseTargetId)} - FOR UPDATE NOWAIT - `, - ); - -/** - * Gets the current release for a specific release target - */ -const getCurrentRelease = async (tx: Tx, releaseTargetId: string) => { - const currentRelease = await tx - .select() - .from(schema.release) - .innerJoin( - schema.versionRelease, - eq(schema.release.versionReleaseId, schema.versionRelease.id), - ) - .innerJoin( - schema.variableSetRelease, - eq(schema.release.variableReleaseId, schema.variableSetRelease.id), - ) - .where(eq(schema.versionRelease.releaseTargetId, releaseTargetId)) - .orderBy(desc(schema.release.createdAt)) - .limit(1) - .then(takeFirstOrNull); - - if (currentRelease == null) return null; - - return { - ...currentRelease.release, - currentVersionRelease: currentRelease.version_release, - currentVariableRelease: currentRelease.variable_set_release, - }; -}; - -const getHasAnythingChanged = ( - currentRelease: { - currentVersionRelease: { id: string }; - currentVariableRelease: { id: string }; - }, - newRelease: { versionReleaseId: string; variableReleaseId: string }, -) => { - const isVersionUnchanged = - currentRelease.currentVersionRelease.id === newRelease.versionReleaseId; - const areVariablesUnchanged = - currentRelease.currentVariableRelease.id === newRelease.variableReleaseId; - return !isVersionUnchanged || !areVariablesUnchanged; -}; - -const insertNewRelease = async ( - tx: Tx, - versionReleaseId: string, - variableReleaseId: string, -) => - tx - .insert(schema.release) - .values({ versionReleaseId, variableReleaseId }) - .returning() - .then(takeFirst); - -/** - * Worker that evaluates a release target and creates necessary releases and jobs - * Only runs in development environment - * Uses mutex to prevent concurrent evaluations of the same target - */ -export const evaluateReleaseTargetWorker = createWorker( - Channel.EvaluateReleaseTarget, - withSpan("evaluateReleaseTarget", async (span, job) => { - const data = job.data; - const skipDuplicateCheck = data.skipDuplicateCheck ?? false; - - span.setAttribute("resource.id", data.resourceId); - span.setAttribute("environment.id", data.environmentId); - span.setAttribute("deployment.id", data.deploymentId); - span.setAttribute("skipDuplicateCheck", skipDuplicateCheck); - - try { - const release = await db.transaction(async (tx) => { - const releaseTarget = await getReleaseTarget(tx, data); - await acquireReleaseTargetLock(tx, releaseTarget.id); - - const { versionEvaluateOptions } = data; - const [versionRelease, variableRelease] = await Promise.all([ - handleVersionRelease(tx, releaseTarget, versionEvaluateOptions), - handleVariableRelease(tx, releaseTarget), - ]); - - if (versionRelease == null) return; - - const currentRelease = await getCurrentRelease(tx, releaseTarget.id); - if (currentRelease == null) - return insertNewRelease(tx, versionRelease.id, variableRelease.id); - - const hasAnythingChanged = getHasAnythingChanged(currentRelease, { - versionReleaseId: versionRelease.id, - variableReleaseId: variableRelease.id, - }); - - if (!hasAnythingChanged) return currentRelease; - - return insertNewRelease(tx, versionRelease.id, variableRelease.id); - }); - - if (release == null) return; - - // Check if a job already exists for this release - const existingReleaseJob = await db.query.releaseJob.findFirst({ - where: eq(schema.releaseJob.releaseId, release.id), - }); - - if (existingReleaseJob != null && !skipDuplicateCheck) return; - - // If no job exists yet, create one and dispatch it - const newReleaseJob = await db.transaction(async (tx) => - createReleaseJob(tx, release), - ); - - log.info("Created release job", { - releaseId: release.id, - job: newReleaseJob, - }); - getQueue(Channel.DispatchJob).add(newReleaseJob.id, { - jobId: newReleaseJob.id, - }); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - const isReleaseTargetNotCommittedYet = e.code === "23503"; - if (isRowLocked || isReleaseTargetNotCommittedYet) { - dispatchQueueJob().toEvaluate().releaseTargets([job.data]); - return; - } - const isJobAgentError = - e.message === "Deployment has no Job Agent" || - e === "Deployment has no Job Agent"; - if (!isJobAgentError) - log.error("Failed to evaluate release target", { error: e }); - throw e; - } - }), - // Member intensive work, attemp to reduce crashing - { concurrency: 10 }, -); diff --git a/apps/event-worker/src/workers/index.ts b/apps/event-worker/src/workers/index.ts deleted file mode 100644 index d52533bf6..000000000 --- a/apps/event-worker/src/workers/index.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { ChannelMap } from "@ctrlplane/events"; -import type { Worker } from "bullmq"; - -import { Channel } from "@ctrlplane/events"; - -import { computeDeploymentResourceSelectorWorkerEvent } from "./compute-deployment-resource-selector.js"; -import { computeEnvironmentResourceSelectorWorkerEvent } from "./compute-environment-resource-selector.js"; -import { computePolicyTargetReleaseTargetSelectorWorkerEvent } from "./compute-policy-target-release-target-selector.js"; -import { computeSystemsReleaseTargetsWorker } from "./compute-systems-release-targets.js"; -import { computeWorkspacePolicyTargetsWorker } from "./compute-workspace-policy-targets.js"; -import { deleteDeploymentWorker } from "./delete-deployment.js"; -import { deleteEnvironmentWorker } from "./delete-environment.js"; -import { deleteResourceWorker } from "./delete-resource.js"; -import { deletedReleaseTargetWorker } from "./deleted-release-target.js"; -import { evaluateReleaseTargetWorker } from "./evaluate-release-target.js"; -import { dispatchJobWorker } from "./job-dispatch/index.js"; -import { updateJobWorker } from "./job-update/index.js"; -import { newDeploymentVersionWorker } from "./new-deployment-version.js"; -import { newDeploymentWorker } from "./new-deployment.js"; -import { newEnvironmentWorker } from "./new-environment.js"; -import { newPolicyWorker } from "./new-policy.js"; -import { newResourceWorker } from "./new-resource.js"; -import { resourceScanWorker } from "./resource-scan/index.js"; -import { updateDeploymentVariableWorker } from "./update-deployment-variable.js"; -import { updateDeploymentWorker } from "./update-deployment.js"; -import { updateEnvironmentWorker } from "./update-environment.js"; -import { updatePolicyWorker } from "./update-policy.js"; -import { updateResourceVariableWorker } from "./update-resource-variable.js"; -import { updatedResourceWorker } from "./updated-resources/index.js"; - -type Workers = { - [K in T]: Worker | null; -}; - -export const workers: Workers = { - [Channel.NewDeployment]: newDeploymentWorker, - [Channel.NewDeploymentVersion]: newDeploymentVersionWorker, - [Channel.NewEnvironment]: newEnvironmentWorker, - [Channel.NewResource]: newResourceWorker, - [Channel.NewPolicy]: newPolicyWorker, - - [Channel.UpdateEnvironment]: updateEnvironmentWorker, - [Channel.UpdateDeployment]: updateDeploymentWorker, - [Channel.UpdatedResource]: updatedResourceWorker, - [Channel.UpdateDeploymentVariable]: updateDeploymentVariableWorker, - [Channel.UpdateResourceVariable]: updateResourceVariableWorker, - [Channel.UpdatePolicy]: updatePolicyWorker, - [Channel.UpdateJob]: updateJobWorker, - - [Channel.EvaluateReleaseTarget]: evaluateReleaseTargetWorker, - [Channel.DispatchJob]: dispatchJobWorker, - [Channel.ResourceScan]: resourceScanWorker, - - [Channel.DeleteResource]: deleteResourceWorker, - [Channel.DeleteDeployment]: deleteDeploymentWorker, - [Channel.DeleteEnvironment]: deleteEnvironmentWorker, - [Channel.DeletedReleaseTarget]: deletedReleaseTargetWorker, - - [Channel.ComputeEnvironmentResourceSelector]: - computeEnvironmentResourceSelectorWorkerEvent, - [Channel.ComputeDeploymentResourceSelector]: - computeDeploymentResourceSelectorWorkerEvent, - [Channel.ComputePolicyTargetReleaseTargetSelector]: - computePolicyTargetReleaseTargetSelectorWorkerEvent, - [Channel.ComputeWorkspacePolicyTargets]: computeWorkspacePolicyTargetsWorker, - [Channel.ComputeSystemsReleaseTargets]: computeSystemsReleaseTargetsWorker, -}; diff --git a/apps/event-worker/src/workers/job-dispatch/github.ts b/apps/event-worker/src/workers/job-dispatch/github.ts deleted file mode 100644 index 4c3be8a4a..000000000 --- a/apps/event-worker/src/workers/job-dispatch/github.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { Job } from "@ctrlplane/db/schema"; -import _ from "lodash"; - -import { and, eq, takeFirstOrNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as SCHEMA from "@ctrlplane/db/schema"; -import { updateJob } from "@ctrlplane/job-dispatch"; -import { logger } from "@ctrlplane/logger"; -import { configSchema } from "@ctrlplane/validators/github"; -import { JobStatus } from "@ctrlplane/validators/jobs"; - -import { getInstallationOctokit } from "../../github-utils.js"; - -const log = logger.child({ module: "github-job-dispatch" }); - -const getGithubEntity = async ( - jobId: string, - installationId: number, - owner: string, -) => { - const runbookGhEntityPromise = db - .select() - .from(SCHEMA.githubEntity) - .innerJoin( - SCHEMA.workspace, - eq(SCHEMA.githubEntity.workspaceId, SCHEMA.workspace.id), - ) - .innerJoin( - SCHEMA.system, - eq(SCHEMA.system.workspaceId, SCHEMA.workspace.id), - ) - .innerJoin(SCHEMA.runbook, eq(SCHEMA.runbook.systemId, SCHEMA.system.id)) - .innerJoin( - SCHEMA.runbookJobTrigger, - eq(SCHEMA.runbookJobTrigger.runbookId, SCHEMA.runbook.id), - ) - .where( - and( - eq(SCHEMA.githubEntity.installationId, installationId), - eq(SCHEMA.githubEntity.slug, owner), - eq(SCHEMA.runbookJobTrigger.jobId, jobId), - ), - ) - .then(takeFirstOrNull) - .then((r) => r?.github_entity); - - const releaseJobEntityPromise = db - .select() - .from(SCHEMA.release) - .innerJoin( - SCHEMA.releaseJob, - eq(SCHEMA.release.id, SCHEMA.releaseJob.releaseId), - ) - .innerJoin( - SCHEMA.versionRelease, - eq(SCHEMA.release.versionReleaseId, SCHEMA.versionRelease.id), - ) - .innerJoin( - SCHEMA.releaseTarget, - eq(SCHEMA.versionRelease.releaseTargetId, SCHEMA.releaseTarget.id), - ) - .innerJoin( - SCHEMA.resource, - eq(SCHEMA.releaseTarget.resourceId, SCHEMA.resource.id), - ) - .innerJoin( - SCHEMA.githubEntity, - eq(SCHEMA.resource.workspaceId, SCHEMA.githubEntity.workspaceId), - ) - .where( - and( - eq(SCHEMA.githubEntity.installationId, installationId), - eq(SCHEMA.githubEntity.slug, owner), - eq(SCHEMA.releaseJob.jobId, jobId), - ), - ) - .then(takeFirstOrNull) - .then((r) => r?.github_entity); - - const [runbookGhEntity, releaseJobEntity] = await Promise.all([ - runbookGhEntityPromise, - releaseJobEntityPromise, - ]); - - return runbookGhEntity ?? releaseJobEntity; -}; - -const getReleaseJobAgentConfig = (jobId: string) => - db - .select({ jobAgentConfig: SCHEMA.deploymentVersion.jobAgentConfig }) - .from(SCHEMA.releaseJob) - .innerJoin( - SCHEMA.release, - eq(SCHEMA.releaseJob.releaseId, SCHEMA.release.id), - ) - .innerJoin( - SCHEMA.versionRelease, - eq(SCHEMA.release.versionReleaseId, SCHEMA.versionRelease.id), - ) - .innerJoin( - SCHEMA.deploymentVersion, - eq(SCHEMA.versionRelease.versionId, SCHEMA.deploymentVersion.id), - ) - .where(eq(SCHEMA.releaseJob.jobId, jobId)) - .then(takeFirstOrNull) - .then((r) => r?.jobAgentConfig); - -export const dispatchGithubJob = async (je: Job) => { - log.info(`Dispatching github job ${je.id}...`); - - const config = je.jobAgentConfig; - const parsed = configSchema.safeParse(config); - if (!parsed.success) { - log.error( - `Invalid job agent config for job ${je.id}: ${parsed.error.message}`, - ); - await updateJob(je.id, { - status: JobStatus.InvalidJobAgent, - message: `Invalid job agent config for job ${je.id}: ${parsed.error.message}`, - }); - return; - } - - const { data: parsedConfig } = parsed; - const releaseJobAgentConfig = await getReleaseJobAgentConfig(je.id); - const mergedConfig = _.merge(parsedConfig, releaseJobAgentConfig ?? {}); - - const ghEntity = await getGithubEntity( - je.id, - mergedConfig.installationId, - mergedConfig.owner, - ); - if (ghEntity == null) { - log.error(`GitHub entity not found for job ${je.id}`); - return updateJob(je.id, { - status: JobStatus.InvalidIntegration, - message: `GitHub entity not found for job ${je.id}`, - }); - } - - const octokit = getInstallationOctokit(ghEntity.installationId); - if (octokit == null) { - log.error(`GitHub bot not configured for job ${je.id}`); - return updateJob(je.id, { - status: JobStatus.InvalidJobAgent, - message: "GitHub bot not configured", - }); - } - - const installationToken = (await octokit.auth({ - type: "installation", - installationId: parsedConfig.installationId, - })) as { token: string }; - - const headers = { - "X-GitHub-Api-Version": "2022-11-28", - authorization: `Bearer ${installationToken.token}`, - }; - - const ref = - mergedConfig.ref ?? - (await octokit.rest.repos - .get({ ...mergedConfig, headers }) - .then((r) => r.data.default_branch) - .catch((e) => { - logger.error(`Failed to get ref for github action job ${je.id}`, { - error: e, - }); - return null; - })); - - if (ref == null) { - log.error(`Failed to get ref for github action job ${je.id}`); - return updateJob(je.id, { - status: JobStatus.InvalidJobAgent, - message: "Failed to get ref for github action job", - }); - } - - log.info(`Creating workflow dispatch for job ${je.id}...`, { - owner: mergedConfig.owner, - repo: mergedConfig.repo, - workflow_id: mergedConfig.workflowId, - ref, - inputs: { job_id: je.id }, - }); - - try { - await octokit.actions.createWorkflowDispatch({ - owner: mergedConfig.owner, - repo: mergedConfig.repo, - workflow_id: mergedConfig.workflowId, - ref, - inputs: { job_id: je.id }, - headers, - }); - } catch (e) { - const error = e instanceof Error ? e.message : String(e); - log.error(`Failed to create workflow dispatch for job ${je.id}`, { - error, - }); - return updateJob(je.id, { - status: JobStatus.InvalidJobAgent, - message: `Failed to dispatch github action job ${je.id}: ${error} to agent (installation: ${ghEntity.installationId}, owner: ${ghEntity.slug}, repo: ${mergedConfig.repo}, workflow: ${mergedConfig.workflowId}, ref: ${ref})`, - }); - } -}; diff --git a/apps/event-worker/src/workers/job-dispatch/index.ts b/apps/event-worker/src/workers/job-dispatch/index.ts deleted file mode 100644 index 67091fed6..000000000 --- a/apps/event-worker/src/workers/job-dispatch/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { eq } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker } from "@ctrlplane/events"; -import { updateJob } from "@ctrlplane/job-dispatch"; -import { JobAgentType, JobStatus } from "@ctrlplane/validators/jobs"; - -import { dispatchGithubJob } from "./github.js"; - -export const dispatchJobWorker = createWorker( - Channel.DispatchJob, - async (queueJob) => { - const { data } = queueJob; - const { jobId } = data; - - const job = await db.query.job.findFirst({ - where: eq(schema.job.id, jobId), - with: { agent: true }, - }); - - if (job == null) { - queueJob.log(`Job ${jobId} not found`); - return; - } - - const { agent } = job; - if (agent == null) { - queueJob.log(`Job ${jobId} has no agent`); - updateJob(job.id, { - status: JobStatus.InvalidJobAgent, - message: `Job has no agent`, - }); - return; - } - - if (agent.type === String(JobAgentType.GithubApp)) { - queueJob.log(`Dispatching job ${jobId} to GitHub app`); - try { - await dispatchGithubJob(job); - } catch (error: any) { - await updateJob(job.id, { - status: JobStatus.InvalidIntegration, - message: `Error dispatching job to GitHub app: ${error}`, - }); - throw error; - } - } - }, -); diff --git a/apps/event-worker/src/workers/job-update/concurrency.ts b/apps/event-worker/src/workers/job-update/concurrency.ts deleted file mode 100644 index b26013624..000000000 --- a/apps/event-worker/src/workers/job-update/concurrency.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { and, eq, inArray, ne } from "@ctrlplane/db"; -import * as schema from "@ctrlplane/db/schema"; - -export const getReleaseTargetsInConcurrencyGroup = async ( - db: Tx, - policyIds: string[], - jobReleaseTargetId: string, -) => - db - .select() - .from(schema.releaseTarget) - .innerJoin( - schema.computedPolicyTargetReleaseTarget, - eq( - schema.computedPolicyTargetReleaseTarget.releaseTargetId, - schema.releaseTarget.id, - ), - ) - .innerJoin( - schema.policyTarget, - eq( - schema.computedPolicyTargetReleaseTarget.policyTargetId, - schema.policyTarget.id, - ), - ) - .where( - and( - ne(schema.releaseTarget.id, jobReleaseTargetId), - inArray(schema.policyTarget.policyId, policyIds), - ), - ) - .then((rows) => rows.map((row) => row.release_target)); diff --git a/apps/event-worker/src/workers/job-update/db-update-job.ts b/apps/event-worker/src/workers/job-update/db-update-job.ts deleted file mode 100644 index a24b962b6..000000000 --- a/apps/event-worker/src/workers/job-update/db-update-job.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { eq, takeFirst } from "@ctrlplane/db"; -import * as schema from "@ctrlplane/db/schema"; -import { exitedStatus, JobStatus } from "@ctrlplane/validators/jobs"; - -const getStartedAt = ( - jobBeforeUpdate: schema.Job, - updates: schema.UpdateJob, -) => { - if (updates.startedAt != null) return updates.startedAt; - if (jobBeforeUpdate.startedAt != null) return jobBeforeUpdate.startedAt; - const isPreviousStatusPending = jobBeforeUpdate.status === JobStatus.Pending; - const isCurrentStatusPending = updates.status === JobStatus.Pending; - if (isPreviousStatusPending && !isCurrentStatusPending) return new Date(); - return null; -}; - -const getCompletedAt = ( - jobBeforeUpdate: schema.Job, - updates: schema.UpdateJob, -) => { - if (updates.completedAt != null) return updates.completedAt; - if (jobBeforeUpdate.completedAt != null) return jobBeforeUpdate.completedAt; - const isPreviousStatusExited = exitedStatus.includes( - jobBeforeUpdate.status as JobStatus, - ); - const isCurrentStatusExited = - updates.status != null && - exitedStatus.includes(updates.status as JobStatus); - - if (!isPreviousStatusExited && isCurrentStatusExited) return new Date(); - return null; -}; - -export const dbUpdateJob = async ( - db: Tx, - jobId: string, - jobBeforeUpdate: schema.Job, - data: schema.UpdateJob, -) => { - const { updatedAt: _updatedAt } = data; - const startedAtResult = getStartedAt(jobBeforeUpdate, data); - const completedAtResult = getCompletedAt(jobBeforeUpdate, data); - const startedAt = startedAtResult != null ? new Date(startedAtResult) : null; - const completedAt = - completedAtResult != null ? new Date(completedAtResult) : null; - const updatedAt = _updatedAt != null ? new Date(_updatedAt) : undefined; - const updates = { ...data, startedAt, completedAt, updatedAt }; - - return db - .update(schema.job) - .set(updates) - .where(eq(schema.job.id, jobId)) - .returning() - .then(takeFirst); -}; diff --git a/apps/event-worker/src/workers/job-update/index.ts b/apps/event-worker/src/workers/job-update/index.ts deleted file mode 100644 index f373bfc78..000000000 --- a/apps/event-worker/src/workers/job-update/index.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { eq, sql } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { - Channel, - createWorker, - dispatchQueueJob, - getQueue, -} from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; -import { - exitedStatus, - failedStatuses, - JobStatus, -} from "@ctrlplane/validators/jobs"; - -import { dbUpdateJob } from "./db-update-job.js"; -import { updateJobMetadata } from "./job-metadata.js"; -import { getNumRetryAttempts, retryJob } from "./job-retry.js"; -import { getMatchedPolicies } from "./matched-policies.js"; -import { triggerDependentTargets } from "./trigger-dependent-targets.js"; -import { getReleaseTarget } from "./utils.js"; - -const log = logger.child({ worker: "job-update" }); - -const getIsJobJustCompleted = ( - previousStatus: JobStatus, - newStatus: JobStatus, -) => { - const isPreviousStatusExited = exitedStatus.includes(previousStatus); - const isNewStatusExited = exitedStatus.includes(newStatus); - return !isPreviousStatusExited && isNewStatusExited; -}; - -export const updateJobWorker = createWorker(Channel.UpdateJob, async (job) => { - const { jobId, data, metadata } = job.data; - - const jobBeforeUpdate = await db.query.job.findFirst({ - where: eq(schema.job.id, jobId), - with: { metadata: true }, - }); - if (jobBeforeUpdate == null) throw new Error("Job not found"); - - try { - const job = await db.transaction(async (tx) => { - await tx.execute( - sql` - SELECT * FROM ${schema.job} - WHERE ${eq(schema.job.id, jobId)} - FOR UPDATE NOWAIT - `, - ); - - const updatedJob = await dbUpdateJob(tx, jobId, jobBeforeUpdate, data); - if (metadata != null) - await updateJobMetadata(tx, jobId, jobBeforeUpdate.metadata, metadata); - - const isJobJustCompleted = getIsJobJustCompleted( - jobBeforeUpdate.status as JobStatus, - updatedJob.status as JobStatus, - ); - if (!isJobJustCompleted) return null; - - const releaseTargetResult = await getReleaseTarget(tx, jobId); - if (releaseTargetResult == null) return null; - const { release_target: releaseTarget } = releaseTargetResult; - - const matchedPolicies = await getMatchedPolicies(tx, releaseTarget); - const isJobFailed = failedStatuses.includes( - updatedJob.status as JobStatus, - ); - const firstRetryPolicy = matchedPolicies.find( - (p) => p.retry != null, - )?.retry; - const numRetryAttempts = await getNumRetryAttempts(tx, jobId); - const shouldRetry = - isJobFailed && - firstRetryPolicy != null && - numRetryAttempts < firstRetryPolicy.maxRetries; - - if (shouldRetry) { - await retryJob(tx, jobId); - return null; - } - - await dispatchQueueJob().toEvaluate().releaseTargets([releaseTarget]); - - return updatedJob; - }); - - if (job?.status === JobStatus.Successful) - await triggerDependentTargets(job); - } catch (e: any) { - const isRowLocked = e.code === "55P03"; - if (isRowLocked) { - getQueue(Channel.UpdateJob).add(jobId, job.data); - return; - } - - log.error("Failed to update job", { error: e }); - throw e; - } -}); diff --git a/apps/event-worker/src/workers/job-update/job-metadata.ts b/apps/event-worker/src/workers/job-update/job-metadata.ts deleted file mode 100644 index 161ab58cc..000000000 --- a/apps/event-worker/src/workers/job-update/job-metadata.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { buildConflictUpdateColumns, sql } from "@ctrlplane/db"; -import * as schema from "@ctrlplane/db/schema"; -import { logger } from "@ctrlplane/logger"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; - -const updateLinksMetadata = async ( - db: Tx, - jobId: string, - links: any, - existingMetadata: schema.JobMetadata[], -) => { - try { - const updatedLinks = JSON.stringify({ - ...JSON.parse( - existingMetadata.find( - (m) => m.key === String(ReservedMetadataKey.Links), - )?.value ?? "{}", - ), - ...links, - }); - - await db - .insert(schema.jobMetadata) - .values({ jobId, key: ReservedMetadataKey.Links, value: updatedLinks }) - .onConflictDoUpdate({ - target: [schema.jobMetadata.jobId, schema.jobMetadata.key], - set: buildConflictUpdateColumns(schema.jobMetadata, ["value"]), - }); - } catch (e) { - logger.error("Failed to update links metadata", { - error: e, - jobId, - links, - }); - return; - } -}; - -export const updateJobMetadata = async ( - db: Tx, - jobId: string, - existingMetadata: schema.JobMetadata[], - metadata: Record, -) => { - const { [ReservedMetadataKey.Links]: links, ...remainingMetadata } = metadata; - - if (links != null) - await updateLinksMetadata(db, jobId, links, existingMetadata); - - if (Object.keys(remainingMetadata).length > 0) - await db - .insert(schema.jobMetadata) - .values( - Object.entries(remainingMetadata).map(([key, value]) => ({ - jobId, - key, - value: JSON.stringify(value), - })), - ) - .onConflictDoUpdate({ - target: [schema.jobMetadata.jobId, schema.jobMetadata.key], - set: { value: sql`excluded.value` }, - }); -}; diff --git a/apps/event-worker/src/workers/job-update/job-retry.ts b/apps/event-worker/src/workers/job-update/job-retry.ts deleted file mode 100644 index 49cf63f3b..000000000 --- a/apps/event-worker/src/workers/job-update/job-retry.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { count, eq, takeFirst } from "@ctrlplane/db"; -import { createReleaseJob } from "@ctrlplane/db/queries"; -import * as schema from "@ctrlplane/db/schema"; -import { dispatchQueueJob } from "@ctrlplane/events"; - -export const getNumRetryAttempts = async (db: Tx, jobId: string) => { - const { release } = await db - .select() - .from(schema.release) - .innerJoin( - schema.releaseJob, - eq(schema.releaseJob.releaseId, schema.release.id), - ) - .where(eq(schema.releaseJob.jobId, jobId)) - .then(takeFirst); - - return db - .select({ count: count() }) - .from(schema.releaseJob) - .where(eq(schema.releaseJob.releaseId, release.id)) - .then(takeFirst) - .then((row) => row.count); -}; - -const getReleaseInfo = async (db: Tx, jobId: string) => - db - .select() - .from(schema.release) - .innerJoin( - schema.releaseJob, - eq(schema.releaseJob.releaseId, schema.release.id), - ) - .innerJoin( - schema.versionRelease, - eq(schema.release.versionReleaseId, schema.versionRelease.id), - ) - .innerJoin( - schema.variableSetRelease, - eq(schema.release.variableReleaseId, schema.variableSetRelease.id), - ) - .where(eq(schema.releaseJob.jobId, jobId)) - .then(takeFirst); - -export const retryJob = async (db: Tx, jobId: string) => { - const { release, version_release, variable_set_release } = - await getReleaseInfo(db, jobId); - - const releaseId = release.id; - const versionReleaseId = version_release.id; - const variableReleaseId = variable_set_release.id; - - const newReleaseJob = await db.transaction((tx) => - createReleaseJob(tx, { - id: releaseId, - versionReleaseId, - variableReleaseId, - }), - ); - - await dispatchQueueJob().toDispatch().ctrlplaneJob(newReleaseJob.id); -}; diff --git a/apps/event-worker/src/workers/job-update/matched-policies.ts b/apps/event-worker/src/workers/job-update/matched-policies.ts deleted file mode 100644 index ee047eb30..000000000 --- a/apps/event-worker/src/workers/job-update/matched-policies.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { desc, eq } from "@ctrlplane/db"; -import * as schema from "@ctrlplane/db/schema"; - -export const getMatchedPolicies = async ( - db: Tx, - releaseTarget: schema.ReleaseTarget, -) => - db - .select({ - policyId: schema.policy.id, - concurrency: schema.policyRuleConcurrency, - retry: schema.policyRuleRetry, - }) - .from(schema.policy) - .innerJoin( - schema.policyTarget, - eq(schema.policyTarget.policyId, schema.policy.id), - ) - .innerJoin( - schema.computedPolicyTargetReleaseTarget, - eq( - schema.computedPolicyTargetReleaseTarget.policyTargetId, - schema.policyTarget.id, - ), - ) - .leftJoin( - schema.policyRuleRetry, - eq(schema.policyRuleRetry.policyId, schema.policy.id), - ) - .leftJoin( - schema.policyRuleConcurrency, - eq(schema.policyRuleConcurrency.policyId, schema.policy.id), - ) - .where( - eq( - schema.computedPolicyTargetReleaseTarget.releaseTargetId, - releaseTarget.id, - ), - ) - .orderBy(desc(schema.policy.priority)); diff --git a/apps/event-worker/src/workers/job-update/trigger-dependent-targets.ts b/apps/event-worker/src/workers/job-update/trigger-dependent-targets.ts deleted file mode 100644 index 86738823b..000000000 --- a/apps/event-worker/src/workers/job-update/trigger-dependent-targets.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { isPresent } from "ts-is-present"; - -import { - and, - eq, - inArray, - selector, - takeFirst, - takeFirstOrNull, -} from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import { getResourceChildren } from "@ctrlplane/db/queries"; -import * as schema from "@ctrlplane/db/schema"; -import { dispatchQueueJob } from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; - -import { getReleaseTarget } from "./utils.js"; - -const log = logger.child({ - worker: "job-update", - handler: "trigger-dependent-targets", -}); - -const getVersion = (versionId: string) => - db - .select() - .from(schema.deploymentVersion) - .where(eq(schema.deploymentVersion.id, versionId)) - .then(takeFirst); - -const getDependencyVersionMatch = ( - dependency: schema.VersionDependency, - versionId: string, -) => - db - .select() - .from(schema.deploymentVersion) - .where( - and( - eq(schema.deploymentVersion.id, versionId), - selector() - .query() - .deploymentVersions() - .where(dependency.versionSelector) - .sql(), - ), - ) - .then(takeFirstOrNull); - -const getNewlySatisfiedDependencies = async ( - version: schema.DeploymentVersion, -) => { - const { deploymentId } = version; - const dependenciesOfThisDeployment = await db - .select() - .from(schema.versionDependency) - .where(eq(schema.versionDependency.deploymentId, deploymentId)); - - return Promise.all( - dependenciesOfThisDeployment.map(async (dependency) => { - const versionMatch = await getDependencyVersionMatch( - dependency, - version.id, - ); - - return versionMatch != null ? dependency : null; - }), - ).then((deps) => deps.filter(isPresent)); -}; - -const getReleaseTargetsToEvaluate = async ( - resourceIds: string[], - newlySatisfiedDependencies: schema.VersionDependency[], -) => - db - .selectDistinctOn([schema.releaseTarget.id]) - .from(schema.deploymentVersion) - .innerJoin( - schema.deployment, - eq(schema.deploymentVersion.deploymentId, schema.deployment.id), - ) - .innerJoin( - schema.releaseTarget, - eq(schema.releaseTarget.deploymentId, schema.deployment.id), - ) - .where( - and( - inArray( - schema.deploymentVersion.id, - newlySatisfiedDependencies.map((d) => d.versionId), - ), - inArray(schema.releaseTarget.resourceId, resourceIds), - ), - ) - .then((rows) => rows.map((row) => row.release_target)); - -export const triggerDependentTargets = async (job: schema.Job) => { - try { - const releaseTargetResult = await getReleaseTarget(db, job.id); - if (releaseTargetResult == null) return; - const { resourceId } = releaseTargetResult.release_target; - const { versionId } = releaseTargetResult.version_release; - - const version = await getVersion(versionId); - const newlySatisfiedDependencies = - await getNewlySatisfiedDependencies(version); - if (newlySatisfiedDependencies.length === 0) return; - - const childResources = await getResourceChildren(db, resourceId); - if (childResources.length === 0) return; - - const childResourceIds = childResources.map(({ target }) => target.id); - const releaseTargetsToEvaluate = await getReleaseTargetsToEvaluate( - [resourceId, ...childResourceIds], - newlySatisfiedDependencies, - ); - - await dispatchQueueJob() - .toEvaluate() - .releaseTargets(releaseTargetsToEvaluate); - } catch (error) { - log.error("Error triggering dependent targets", { error }); - } -}; diff --git a/apps/event-worker/src/workers/job-update/utils.ts b/apps/event-worker/src/workers/job-update/utils.ts deleted file mode 100644 index 6eb43e1c8..000000000 --- a/apps/event-worker/src/workers/job-update/utils.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; - -import { eq, takeFirstOrNull } from "@ctrlplane/db"; -import * as schema from "@ctrlplane/db/schema"; - -export const getReleaseTarget = (db: Tx, jobId: string) => - db - .select() - .from(schema.releaseJob) - .innerJoin( - schema.release, - eq(schema.releaseJob.releaseId, schema.release.id), - ) - .innerJoin( - schema.versionRelease, - eq(schema.release.versionReleaseId, schema.versionRelease.id), - ) - .innerJoin( - schema.releaseTarget, - eq(schema.versionRelease.releaseTargetId, schema.releaseTarget.id), - ) - .where(eq(schema.releaseJob.jobId, jobId)) - .then(takeFirstOrNull); diff --git a/apps/event-worker/src/workers/new-deployment-version.ts b/apps/event-worker/src/workers/new-deployment-version.ts deleted file mode 100644 index 1fcedf8eb..000000000 --- a/apps/event-worker/src/workers/new-deployment-version.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { eq } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -/** - * Worker that processes new deployment version events. - * When a new deployment version is created, simply grab all release targets for the deployment - * and add them to the evaluation queue. - * - * @param {Job} job - The deployment version data - * @returns {Promise} A promise that resolves when processing is complete - */ - -export const newDeploymentVersionWorker = createWorker( - Channel.NewDeploymentVersion, - async ({ data: version }) => { - const releaseTargets = await db.query.releaseTarget.findMany({ - where: eq(schema.releaseTarget.deploymentId, version.deploymentId), - }); - await dispatchQueueJob() - .toEvaluate() - .releaseTargets(releaseTargets, { - versionEvaluateOptions: { versions: [version] }, - }); - }, -); diff --git a/apps/event-worker/src/workers/new-deployment.ts b/apps/event-worker/src/workers/new-deployment.ts deleted file mode 100644 index e0850c9fb..000000000 --- a/apps/event-worker/src/workers/new-deployment.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -export const newDeploymentWorker = createWorker( - Channel.NewDeployment, - async (job) => { - const { data: deployment } = job; - await dispatchQueueJob() - .toCompute() - .deployment(deployment) - .resourceSelector(); - }, -); diff --git a/apps/event-worker/src/workers/new-environment.ts b/apps/event-worker/src/workers/new-environment.ts deleted file mode 100644 index 82f569a12..000000000 --- a/apps/event-worker/src/workers/new-environment.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -export const newEnvironmentWorker = createWorker( - Channel.NewEnvironment, - async (job) => { - const { data: environment } = job; - await dispatchQueueJob() - .toCompute() - .environment(environment) - .resourceSelector(); - }, -); diff --git a/apps/event-worker/src/workers/new-policy.ts b/apps/event-worker/src/workers/new-policy.ts deleted file mode 100644 index 97579734a..000000000 --- a/apps/event-worker/src/workers/new-policy.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { eq } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -export const newPolicyWorker = createWorker(Channel.NewPolicy, async (job) => { - const policyTargets = await db.query.policyTarget.findMany({ - where: eq(schema.policyTarget.policyId, job.data.id), - }); - - for (const policyTarget of policyTargets) - dispatchQueueJob() - .toCompute() - .policyTarget(policyTarget) - .releaseTargetSelector(); -}); diff --git a/apps/event-worker/src/workers/new-resource.ts b/apps/event-worker/src/workers/new-resource.ts deleted file mode 100644 index fbd493d4d..000000000 --- a/apps/event-worker/src/workers/new-resource.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { eq } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -/** - * Worker that processes new resource events. - * - * When a new resource is created, perform the following steps: - * 1. Recompute all environments' and deployments' resource selectors - * 2. Upsert release targets for the resource - * 3. Recompute all policy targets' computed release targets - * 4. Add all upserted release targets to the evaluation queue - * - * @param {Job} job - The resource data - * @returns {Promise} A promise that resolves when processing is complete - */ -export const newResourceWorker = createWorker( - Channel.NewResource, - async ({ data: resource }) => { - const { workspaceId } = resource; - - const systems = await db.query.system.findMany({ - where: eq(schema.system.workspaceId, workspaceId), - with: { environments: true, deployments: true }, - }); - const environments = systems.flatMap((s) => s.environments); - - for (const environment of environments) { - await dispatchQueueJob() - .toCompute() - .environment(environment) - .resourceSelector(); - } - - const deployments = systems.flatMap((s) => s.deployments); - for (const deployment of deployments) { - await dispatchQueueJob() - .toCompute() - .deployment(deployment) - .resourceSelector(); - } - }, -); diff --git a/apps/event-worker/src/workers/resource-scan/aws/aws.ts b/apps/event-worker/src/workers/resource-scan/aws/aws.ts deleted file mode 100644 index e128a7d56..000000000 --- a/apps/event-worker/src/workers/resource-scan/aws/aws.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { Credentials } from "@aws-sdk/client-sts"; -import type { AwsCredentialIdentity } from "@smithy/types"; -import { EC2Client } from "@aws-sdk/client-ec2"; -import { EKSClient } from "@aws-sdk/client-eks"; -import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; - -const sourceClient = new STSClient({ region: "us-east-1" }); - -export class AwsCredentials { - static from(credentials: Credentials) { - return new AwsCredentials(credentials); - } - - private constructor(private readonly credentials: Credentials) {} - - toIdentity(): AwsCredentialIdentity { - if ( - this.credentials.AccessKeyId == null || - this.credentials.SecretAccessKey == null - ) - throw new Error("Missing required AWS credentials"); - - return { - accessKeyId: this.credentials.AccessKeyId, - secretAccessKey: this.credentials.SecretAccessKey, - sessionToken: this.credentials.SessionToken ?? undefined, - }; - } - - ec2(region?: string) { - return new EC2Client({ region, credentials: this.toIdentity() }); - } - - eks(region?: string) { - return new EKSClient({ region, credentials: this.toIdentity() }); - } - - sts(region?: string) { - return new STSClient({ region, credentials: this.toIdentity() }); - } -} - -export const assumeWorkspaceRole = async (roleArn: string) => - assumeRole(sourceClient, roleArn); - -export const assumeRole = async ( - client: STSClient, - roleArn: string, -): Promise => { - const { Credentials: CustomerCredentials } = await client.send( - new AssumeRoleCommand({ - RoleArn: roleArn, - RoleSessionName: "CtrlplaneScanner", - }), - ); - if (CustomerCredentials == null) - throw new Error(`Failed to assume AWS role ${roleArn}`); - return AwsCredentials.from(CustomerCredentials); -}; diff --git a/apps/event-worker/src/workers/resource-scan/aws/eks.ts b/apps/event-worker/src/workers/resource-scan/aws/eks.ts deleted file mode 100644 index dd9f96693..000000000 --- a/apps/event-worker/src/workers/resource-scan/aws/eks.ts +++ /dev/null @@ -1,203 +0,0 @@ -import type { Cluster, EKSClient } from "@aws-sdk/client-eks"; -import type { STSClient } from "@aws-sdk/client-sts"; -import type { ResourceProviderAws, Workspace } from "@ctrlplane/db/schema"; -import type { KubernetesClusterAPIV1 } from "@ctrlplane/validators/resources"; -import { DescribeRegionsCommand } from "@aws-sdk/client-ec2"; -import { - ClusterStatus, - DescribeClusterCommand, - ListClustersCommand, -} from "@aws-sdk/client-eks"; -import _ from "lodash"; -import { isPresent } from "ts-is-present"; - -import { logger } from "@ctrlplane/logger"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; -import { cloudRegionsGeo } from "@ctrlplane/validators/resources"; - -import type { AwsCredentials } from "./aws.js"; -import { omitNullUndefined } from "../../../utils/omit-null-undefined.js"; -import { assumeRole, assumeWorkspaceRole } from "./aws.js"; - -const log = logger.child({ label: "resource-scan/eks" }); - -const convertEksClusterToKubernetesResource = ( - accountId: string, - cluster: Cluster, -): KubernetesClusterAPIV1 => { - const region = cluster.endpoint?.split(".")[2]; - - const partition = - cluster.arn?.split(":")[1] ?? - (region?.startsWith("us-gov-") ? "aws-us-gov" : "aws"); - - const appUrl = `https://${ - partition === "aws-us-gov" - ? `console.${region}.${partition}` - : "console.aws.amazon" - }.com/eks/home?region=${region}#/clusters/${cluster.name}`; - - const version = cluster.version!; - const [major, minor] = version.split("."); - - const { timezone, latitude, longitude } = cloudRegionsGeo[region ?? ""] ?? {}; - - return { - name: cluster.name ?? "", - identifier: cluster.arn ?? "", - version: "kubernetes/v1" as const, - kind: "ClusterAPI" as const, - config: { - name: cluster.name!, - auth: { - method: "aws/eks" as const, - region: region!, - clusterName: cluster.name!, - accountId, - }, - status: cluster.status ?? "UNKNOWN", - server: { - certificateAuthorityData: cluster.certificateAuthority?.data, - endpoint: cluster.endpoint!, - }, - }, - metadata: omitNullUndefined({ - [ReservedMetadataKey.Links]: JSON.stringify({ "AWS Console": appUrl }), - [ReservedMetadataKey.ExternalId]: cluster.arn ?? "", - [ReservedMetadataKey.KubernetesFlavor]: "eks", - [ReservedMetadataKey.KubernetesVersion]: cluster.version, - [ReservedMetadataKey.KubernetesStatus]: - cluster.status === ClusterStatus.ACTIVE - ? "running" - : cluster.status === ClusterStatus.CREATING - ? "creating" - : "unknown", - - [ReservedMetadataKey.LocationTimezone]: timezone, - [ReservedMetadataKey.LocationLatitude]: latitude, - [ReservedMetadataKey.LocationLongitude]: longitude, - - "aws/arn": cluster.arn, - "aws/region": region, - "aws/platform-version": cluster.platformVersion, - "aws/account-id": accountId, - "aws/eks-role-arn": cluster.roleArn, - - "kubernetes/version-major": major, - "kubernetes/version-minor": minor, - - ...(cluster.tags ?? {}), - }), - }; -}; - -const getAwsRegions = async (credentials: AwsCredentials) => - credentials - .ec2() - .send(new DescribeRegionsCommand({})) - .then(({ Regions = [] }) => Regions.map((region) => region.RegionName)); - -const getClusters = async (client: EKSClient) => - client - .send(new ListClustersCommand({})) - .then((response) => response.clusters ?? []); - -const createEksClusterScannerForRegion = ( - client: AwsCredentials, - customerRoleArn: string, -) => { - const accountId = /arn:aws:iam::(\d+):/.exec(customerRoleArn)?.[1]; - if (accountId == null) throw new Error("Missing account ID"); - - return async (region: string) => { - const eksClient = client.eks(region); - const clusters = await getClusters(eksClient); - log.info( - `Found ${clusters.length} clusters for ${customerRoleArn} in region ${region}`, - ); - - return _.chain(clusters) - .map((name) => - eksClient - .send(new DescribeClusterCommand({ name })) - .then(({ cluster }) => cluster), - ) - .thru((promises) => Promise.all(promises)) - .value() - .then((clusterDetails) => - clusterDetails - .filter(isPresent) - .map((cluster) => - convertEksClusterToKubernetesResource(accountId, cluster), - ), - ); - }; -}; - -const scanEksClustersByAssumedRole = async ( - workspaceClient: STSClient, - customerRoleArn: string, -) => { - const client = await assumeRole(workspaceClient, customerRoleArn); - const regions = await getAwsRegions(client); - - log.info( - `Scanning ${regions.length} AWS regions for EKS clusters in account ${customerRoleArn}`, - ); - - const regionalClusterScanner = createEksClusterScannerForRegion( - client, - customerRoleArn, - ); - - return _.chain(regions) - .filter(isPresent) - .map(regionalClusterScanner) - .thru((promises) => Promise.all(promises)) - .value() - .then((results) => results.flat()); -}; - -export const getEksResources = async ( - workspace: Workspace, - config: ResourceProviderAws, -) => { - if (!config.importEks) return []; - const { awsRoleArn: workspaceRoleArn } = workspace; - if (workspaceRoleArn == null) return []; - - log.info( - `Scanning for EKS cluters with assumed role arns ${config.awsRoleArns.join(", ")} using role ${workspaceRoleArn}`, - { - workspaceId: workspace.id, - config, - workspaceRoleArn, - }, - ); - - const credentials = await assumeWorkspaceRole(workspaceRoleArn); - const workspaceStsClient = credentials.sts(); - - const resources = await _.chain(config.awsRoleArns) - .map((customerRoleArn) => - scanEksClustersByAssumedRole(workspaceStsClient, customerRoleArn), - ) - .thru((promises) => Promise.all(promises)) - .value() - .then((results) => results.flat()) - .then((resources) => - resources.map((resource) => ({ - ...resource, - workspaceId: workspace.id, - providerId: config.resourceProviderId, - })), - ); - - const resourceTypes = _.countBy(resources, (resource) => - [resource.kind, resource.version].join("/"), - ); - - log.info(`Found ${resources.length} resources`, { resourceTypes }); - - return resources; -}; diff --git a/apps/event-worker/src/workers/resource-scan/aws/vpc.ts b/apps/event-worker/src/workers/resource-scan/aws/vpc.ts deleted file mode 100644 index 7d7f6435c..000000000 --- a/apps/event-worker/src/workers/resource-scan/aws/vpc.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { Vpc } from "@aws-sdk/client-ec2"; -import type { STSClient } from "@aws-sdk/client-sts"; -import type { ResourceProviderAws, Workspace } from "@ctrlplane/db/schema"; -import type { CloudVPCV1 } from "@ctrlplane/validators/resources"; -import { - DescribeRegionsCommand, - DescribeSubnetsCommand, - DescribeVpcsCommand, -} from "@aws-sdk/client-ec2"; -import _ from "lodash"; -import { isPresent } from "ts-is-present"; - -import { logger } from "@ctrlplane/logger"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; - -import type { AwsCredentials } from "./aws.js"; -import { omitNullUndefined } from "../../../utils/omit-null-undefined.js"; -import { assumeRole, assumeWorkspaceRole } from "./aws.js"; - -const log = logger.child({ label: "resource-scan/aws/vpc" }); - -const convertVpcToCloudResource = ( - accountId: string, - region: string, - vpc: Vpc, - subnets: { - name: string; - region: string; - cidr: string; - type: "public" | "private"; - availabilityZone?: string; - }[] = [], -): CloudVPCV1 => { - const partition = region.startsWith("us-gov-") ? "aws-us-gov" : "aws"; - const appUrl = `https://${ - partition === "aws-us-gov" - ? `console.${region}.${partition}` - : "console.aws.amazon" - }.com/vpcconsole/home?region=${region}#vpcs:search=${vpc.VpcId}`; - - const name = vpc.Tags?.find((tag) => tag.Key === "Name")?.Value ?? vpc.VpcId!; - - return { - name, - identifier: `aws/${accountId}/vpc/${vpc.VpcId}`, - version: "cloud/v1", - kind: "VPC", - config: { - name, - id: vpc.VpcId!, - provider: "aws", - region, - accountId: accountId, - cidr: vpc.CidrBlock, - subnets, - secondaryCidrs: vpc.CidrBlockAssociationSet?.filter( - (assoc) => assoc.CidrBlock !== vpc.CidrBlock, - ).map((assoc) => ({ - cidr: assoc.CidrBlock ?? "", - state: assoc.CidrBlockState?.State?.toLowerCase() ?? "", - })), - }, - metadata: omitNullUndefined({ - [ReservedMetadataKey.ExternalId]: vpc.VpcId, - [ReservedMetadataKey.Links]: JSON.stringify({ "AWS Console": appUrl }), - "aws/region": region, - "aws/state": vpc.State, - "aws/is-default": vpc.IsDefault, - "aws/dhcp-options-id": vpc.DhcpOptionsId, - "aws/instance-tenancy": vpc.InstanceTenancy, - ...(vpc.Tags?.reduce( - (acc, tag) => ({ - ...acc, - [`aws/tag/${tag.Key}`]: tag.Value, - }), - {}, - ) ?? {}), - }), - }; -}; - -const getAwsRegions = async (credentials: AwsCredentials) => - credentials - .ec2() - .send(new DescribeRegionsCommand({})) - .then(({ Regions = [] }) => Regions.map((region) => region.RegionName)); - -const getVpcs = async (client: AwsCredentials, region: string) => { - const ec2Client = client.ec2(region); - const { Vpcs = [] } = await ec2Client.send(new DescribeVpcsCommand({})); - return Vpcs; -}; - -const getSubnets = async ( - client: AwsCredentials, - region: string, - vpcId: string, -): Promise< - { - name: string; - region: string; - cidr: string; - type: "public" | "private"; - availabilityZone: string; - }[] -> => { - const ec2Client = client.ec2(region); - const { Subnets = [] } = await ec2Client.send( - new DescribeSubnetsCommand({ - Filters: [{ Name: "vpc-id", Values: [vpcId] }], - }), - ); - return Subnets.map((subnet) => ({ - name: subnet.SubnetId ?? "", - region, - cidr: subnet.CidrBlock ?? "", - type: subnet.MapPublicIpOnLaunch ? "public" : "private", - availabilityZone: subnet.AvailabilityZone ?? "", - })); -}; - -const createVpcScannerForRegion = ( - client: AwsCredentials, - customerRoleArn: string, -) => { - const accountId = /arn:aws:iam::(\d+):/.exec(customerRoleArn)?.[1]; - if (accountId == null) throw new Error("Missing account ID"); - - return async (region: string) => { - const vpcs = await getVpcs(client, region); - log.info( - `Found ${vpcs.length} VPCs for ${customerRoleArn} in region ${region}`, - ); - - const vpcResources = await Promise.all( - vpcs.map(async (vpc) => { - if (!vpc.VpcId) return null; - const subnets = await getSubnets(client, region, vpc.VpcId); - return convertVpcToCloudResource(accountId, region, vpc, subnets); - }), - ); - - return vpcResources.filter(isPresent); - }; -}; - -const scanVpcsByAssumedRole = async ( - workspaceClient: STSClient, - customerRoleArn: string, -) => { - const client = await assumeRole(workspaceClient, customerRoleArn); - const regions = await getAwsRegions(client); - - log.info( - `Scanning ${regions.length} AWS regions for VPCs in account ${customerRoleArn}`, - ); - - const regionalVpcScanner = createVpcScannerForRegion(client, customerRoleArn); - - return _.chain(regions) - .filter(isPresent) - .map(regionalVpcScanner) - .thru((promises) => Promise.all(promises)) - .value() - .then((results) => results.flat()); -}; - -export const getVpcResources = async ( - workspace: Workspace, - config: ResourceProviderAws, -) => { - if (!config.importVpc) return []; - - const { awsRoleArn: workspaceRoleArn } = workspace; - if (workspaceRoleArn == null) return []; - - log.info( - `Scanning for VPCs with assumed role arns ${config.awsRoleArns.join(", ")} using role ${workspaceRoleArn}`, - { - workspaceId: workspace.id, - config, - workspaceRoleArn, - }, - ); - - const credentials = await assumeWorkspaceRole(workspaceRoleArn); - const workspaceStsClient = credentials.sts(); - - const resources = await _.chain(config.awsRoleArns) - .map((customerRoleArn) => - scanVpcsByAssumedRole(workspaceStsClient, customerRoleArn), - ) - .thru((promises) => Promise.all(promises)) - .value() - .then((results) => results.flat()) - .then((resources) => - resources.map((resource) => ({ - ...resource, - workspaceId: workspace.id, - providerId: config.resourceProviderId, - })), - ); - - log.info(`Found ${resources.length} VPC resources`); - - return resources; -}; diff --git a/apps/event-worker/src/workers/resource-scan/azure/aks.ts b/apps/event-worker/src/workers/resource-scan/azure/aks.ts deleted file mode 100644 index f2c6f8399..000000000 --- a/apps/event-worker/src/workers/resource-scan/azure/aks.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { ManagedCluster } from "@azure/arm-containerservice"; -import { ContainerServiceClient } from "@azure/arm-containerservice"; -import { ClientSecretCredential } from "@azure/identity"; -import { isPresent } from "ts-is-present"; - -import { eq, takeFirstOrNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as SCHEMA from "@ctrlplane/db/schema"; - -import { env } from "../../../config.js"; -import { convertManagedClusterToResource } from "./cluster-to-resource.js"; - -const AZURE_CLIENT_ID = env.AZURE_APP_CLIENT_ID; -const AZURE_CLIENT_SECRET = env.AZURE_APP_CLIENT_SECRET; - -export const getAksResources = async ( - workspace: SCHEMA.Workspace, - azureProvider: SCHEMA.ResourceProviderAzure, -) => { - if (!AZURE_CLIENT_ID || !AZURE_CLIENT_SECRET) - throw new Error("Invalid azure credentials"); - - const tenant = await db - .select() - .from(SCHEMA.azureTenant) - .where(eq(SCHEMA.azureTenant.id, azureProvider.tenantId)) - .then(takeFirstOrNull); - - if (!tenant) throw new Error("Tenant not found"); - - const credential = new ClientSecretCredential( - tenant.tenantId, - AZURE_CLIENT_ID, - AZURE_CLIENT_SECRET, - ); - - const client = new ContainerServiceClient( - credential, - azureProvider.subscriptionId, - ); - - const res = client.managedClusters.list(); - - const clusters: ManagedCluster[] = []; - for await (const cluster of res) { - clusters.push(cluster); - } - - const resourcePromises = clusters.map((cluster) => - convertManagedClusterToResource( - workspace.id, - azureProvider, - tenant.tenantId, - cluster, - client, - ), - ); - const resources = await Promise.all(resourcePromises); - return resources.filter(isPresent); -}; diff --git a/apps/event-worker/src/workers/resource-scan/azure/cluster-to-resource.ts b/apps/event-worker/src/workers/resource-scan/azure/cluster-to-resource.ts deleted file mode 100644 index 55b75ed0a..000000000 --- a/apps/event-worker/src/workers/resource-scan/azure/cluster-to-resource.ts +++ /dev/null @@ -1,238 +0,0 @@ -import type { - ContainerServiceClient, - ManagedCluster, -} from "@azure/arm-containerservice"; -import type * as SCHEMA from "@ctrlplane/db/schema"; -import type { KubernetesClusterAPIV1 } from "@ctrlplane/validators/resources"; -import * as yaml from "js-yaml"; -import { z } from "zod"; - -import { logger } from "@ctrlplane/logger"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; -import { cloudRegionsGeo } from "@ctrlplane/validators/resources"; - -import { omitNullUndefined } from "../../../utils/omit-null-undefined.js"; - -const log = logger.child({ module: "resource-scan/azure" }); - -type ClusterResource = KubernetesClusterAPIV1 & { - workspaceId: string; - providerId: string; -}; - -const cluster = z.object({ - "certificate-authority-data": z.string(), - server: z.string(), -}); -const kubeConfigSchema = z.object({ clusters: z.array(z.object({ cluster })) }); - -const getCertificateAuthorityData = async ( - cluster: ManagedCluster, - resourceGroup: string, - client: ContainerServiceClient, -) => { - try { - const { kubernetesVersion, name } = cluster; - if (!kubernetesVersion || !name) return null; - - const kubeConfigRaw = await client.managedClusters - .getAccessProfile(resourceGroup, name, "clusterAdmin") - .then((profile) => profile.kubeConfig); - if (!kubeConfigRaw) return null; - - const kubeConfigYaml = Buffer.from(kubeConfigRaw).toString("utf-8"); - const kubeConfig = yaml.load(kubeConfigYaml); - - const parsedKubeConfig = kubeConfigSchema.parse(kubeConfig); - const { cluster: parsedCluster } = parsedKubeConfig.clusters[0] ?? {}; - if (!parsedCluster) return null; - return { - endpoint: parsedCluster.server, - certificateAuthorityData: parsedCluster["certificate-authority-data"], - }; - } catch (error) { - log.error("Error getting certificate authority data for cluster", { - cluster: { name: cluster.name, id: cluster.id }, - error, - }); - return null; - } -}; - -export const convertManagedClusterToResource = async ( - workspaceId: string, - provider: SCHEMA.ResourceProviderAzure, - tenantId: string, - cluster: ManagedCluster, - client: ContainerServiceClient, -): Promise => { - if (!cluster.name || !cluster.id) { - log.error("Invalid cluster", { cluster }); - return null; - } - - const resourceGroup = cluster.id.split("/resourcegroups/")[1]?.split("/")[0]; - if (!resourceGroup) { - log.error("Invalid cluster", { cluster }); - return null; - } - - const ca = await getCertificateAuthorityData(cluster, resourceGroup, client); - const { timezone, latitude, longitude } = - cloudRegionsGeo[cluster.location] ?? {}; - return { - workspaceId, - providerId: provider.resourceProviderId, - name: cluster.name, - identifier: cluster.id, - version: "kubernetes/v1", - kind: "ClusterAPI", - config: { - name: cluster.name, - auth: { - method: "azure/aks", - clusterName: cluster.name, - resourceGroup, - tenantId, - subscriptionId: provider.subscriptionId, - }, - server: { ...ca, endpoint: ca?.endpoint ?? cluster.fqdn ?? "" }, - }, - metadata: omitNullUndefined({ - [ReservedMetadataKey.Links]: cluster.azurePortalFqdn - ? JSON.stringify({ "Azure Portal": cluster.azurePortalFqdn }) - : undefined, - [ReservedMetadataKey.ExternalId]: cluster.id ?? "", - [ReservedMetadataKey.KubernetesFlavor]: "aks", - [ReservedMetadataKey.KubernetesVersion]: cluster.currentKubernetesVersion, - [ReservedMetadataKey.KubernetesStatus]: - cluster.provisioningState === "In Progress" - ? "creating" - : cluster.powerState?.code === "Running" - ? "running" - : "unknown", - [ReservedMetadataKey.LocationTimezone]: timezone, - [ReservedMetadataKey.LocationLatitude]: latitude, - [ReservedMetadataKey.LocationLongitude]: longitude, - - "azure/tenant-id": tenantId, - "azure/subscription-id": provider.subscriptionId, - "azure/self-link": cluster.id, - "azure/resource-group": cluster.nodeResourceGroup, - "azure/location": cluster.location, - "azure/platform-version": cluster.kubernetesVersion, - "azure/enable-rbac": cluster.enableRbac, - "azure/enable-oidc": cluster.oidcIssuerProfile?.enabled, - "azure/enable-disk-csi-driver": - cluster.storageProfile?.diskCSIDriver?.enabled, - "azure/enable-file-csi-driver": - cluster.storageProfile?.fileCSIDriver?.enabled, - "azure/enable-snapshot-controller": - cluster.storageProfile?.snapshotController?.enabled, - "azure/service-principal-profile/client-id": - cluster.servicePrincipalProfile?.clientId, - "azure/oidc-issuer-profile/enabled": cluster.oidcIssuerProfile?.enabled, - "azure/oidc-issuer-profile/issuer-url": - cluster.oidcIssuerProfile?.issuerURL, - "azure/storage-profile/disk-csi-driver/enabled": - cluster.storageProfile?.diskCSIDriver?.enabled, - "azure/storage-profile/file-csi-driver/enabled": - cluster.storageProfile?.fileCSIDriver?.enabled, - "azure/storage-profile/snapshot-controller/enabled": - cluster.storageProfile?.snapshotController?.enabled, - "azure/network-profile/network-plugin": - cluster.networkProfile?.networkPlugin, - "azure/network-profile/network-policy": - cluster.networkProfile?.networkPolicy, - "azure/network-profile/pod-cidr": cluster.networkProfile?.podCidr, - "azure/network-profile/service-cidr": cluster.networkProfile?.serviceCidr, - "azure/network-profile/dns-service-ip": - cluster.networkProfile?.dnsServiceIP, - "azure/network-profile/load-balancer-sku": - cluster.networkProfile?.loadBalancerSku, - "azure/network-profile/outbound-type": - cluster.networkProfile?.outboundType, - "azure/addon-profiles/http-application-routing/enabled": - cluster.addonProfiles?.httpApplicationRouting?.enabled, - "azure/addon-profiles/monitoring/enabled": - cluster.addonProfiles?.omsagent?.enabled, - "azure/addon-profiles/azure-policy/enabled": - cluster.addonProfiles?.azurepolicy?.enabled, - "azure/addon-profiles/ingress-application-gateway/enabled": - cluster.addonProfiles?.ingressApplicationGateway?.enabled, - "azure/addon-profiles/open-service-mesh/enabled": - cluster.addonProfiles?.openServiceMesh?.enabled, - "azure/identity/type": cluster.identity?.type, - "azure/provisioning-state": cluster.provisioningState, - "azure/power-state": cluster.powerState?.code, - "azure/max-agent-pools": String(cluster.maxAgentPools), - "azure/dns-prefix": cluster.dnsPrefix, - "azure/fqdn": cluster.fqdn, - "azure/portal-fqdn": cluster.azurePortalFqdn, - "azure/support-plan": cluster.supportPlan, - "azure/disable-local-accounts": cluster.disableLocalAccounts, - "azure/auto-upgrade-profile/channel": - cluster.autoUpgradeProfile?.upgradeChannel, - "azure/sku/name": cluster.sku?.name, - "azure/sku/tier": cluster.sku?.tier, - "azure/agent-pool-profiles": JSON.stringify(cluster.agentPoolProfiles), - "azure/windows-profile/admin-username": - cluster.windowsProfile?.adminUsername, - "azure/windows-profile/enable-csi-proxy": - cluster.windowsProfile?.enableCSIProxy, - "azure/addon-profiles/azure-policy/identity/resource-id": - cluster.addonProfiles?.azurepolicy?.identity?.resourceId, - "azure/addon-profiles/azure-policy/identity/client-id": - cluster.addonProfiles?.azurepolicy?.identity?.clientId, - "azure/addon-profiles/azure-policy/identity/object-id": - cluster.addonProfiles?.azurepolicy?.identity?.objectId, - "azure/addon-profiles/ingress-application-gateway/config/application-gateway-id": - cluster.addonProfiles?.ingressApplicationGateway?.config - ?.applicationGatewayId, - "azure/addon-profiles/ingress-application-gateway/config/effective-application-gateway-id": - cluster.addonProfiles?.ingressApplicationGateway?.config - ?.effectiveApplicationGatewayId, - "azure/addon-profiles/ingress-application-gateway/identity/resource-id": - cluster.addonProfiles?.ingressApplicationGateway?.identity?.resourceId, - "azure/addon-profiles/ingress-application-gateway/identity/client-id": - cluster.addonProfiles?.ingressApplicationGateway?.identity?.clientId, - "azure/addon-profiles/ingress-application-gateway/identity/object-id": - cluster.addonProfiles?.ingressApplicationGateway?.identity?.objectId, - "azure/network-profile/network-dataplane": - cluster.networkProfile?.networkDataplane, - "azure/network-profile/load-balancer-profile/managed-outbound-ips/count": - cluster.networkProfile?.loadBalancerProfile?.managedOutboundIPs?.count, - "azure/network-profile/load-balancer-profile/managed-outbound-ips/count-ipv6": - cluster.networkProfile?.loadBalancerProfile?.managedOutboundIPs - ?.countIPv6, - "azure/network-profile/load-balancer-profile/effective-outbound-ips": - JSON.stringify( - cluster.networkProfile?.loadBalancerProfile?.effectiveOutboundIPs, - ), - "azure/network-profile/load-balancer-profile/allocated-outbound-ports": - cluster.networkProfile?.loadBalancerProfile?.allocatedOutboundPorts, - "azure/network-profile/load-balancer-profile/idle-timeout-in-minutes": - cluster.networkProfile?.loadBalancerProfile?.idleTimeoutInMinutes, - "azure/network-profile/load-balancer-profile/backend-pool-type": - cluster.networkProfile?.loadBalancerProfile?.backendPoolType, - "azure/network-profile/service-cidrs": JSON.stringify( - cluster.networkProfile?.serviceCidrs, - ), - "azure/network-profile/ip-families": JSON.stringify( - cluster.networkProfile?.ipFamilies, - ), - "azure/identity-profile/kubeletidentity/resource-id": - cluster.identityProfile?.kubeletidentity?.resourceId, - "azure/identity-profile/kubeletidentity/client-id": - cluster.identityProfile?.kubeletidentity?.clientId, - "azure/identity-profile/kubeletidentity/object-id": - cluster.identityProfile?.kubeletidentity?.objectId, - "azure/security-profile/defender/enable-defender": - cluster.securityProfile?.defender?.securityMonitoring?.enabled, - "azure/security-profile/defender/log-analytics-workspace-id": - cluster.securityProfile?.defender?.logAnalyticsWorkspaceResourceId, - "azure/security-profile/defender/security-monitoring/enabled": - cluster.securityProfile?.defender?.securityMonitoring?.enabled, - }), - }; -}; diff --git a/apps/event-worker/src/workers/resource-scan/google/client.ts b/apps/event-worker/src/workers/resource-scan/google/client.ts deleted file mode 100644 index b593f4672..000000000 --- a/apps/event-worker/src/workers/resource-scan/google/client.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { AuthClient } from "google-auth-library"; -import { GoogleAuth, Impersonated } from "google-auth-library"; - -import { logger } from "@ctrlplane/logger"; - -const log = logger.child({ label: "resource-scan/gke/google" }); - -export const sourceCredentials = new GoogleAuth({ - scopes: ["https://www.googleapis.com/auth/cloud-platform"], -}); - -const getImpersonatedClient = async (targetPrincipal: string) => - new Impersonated({ - sourceClient: await sourceCredentials.getClient(), - targetPrincipal, - lifetime: 3600, - delegates: [], - targetScopes: ["https://www.googleapis.com/auth/cloud-platform"], - }); - -export const getGoogleClient = async ( - ClientClass: new (options?: any) => T, - targetPrincipal?: string | null, - clientName = "Google client", -): Promise<[T, AuthClient | undefined]> => { - try { - if (targetPrincipal == null) - return [new ClientClass(), await sourceCredentials.getClient()]; - const authClient = await getImpersonatedClient(targetPrincipal); - return [new ClientClass({ authClient }), authClient]; - } catch (error: any) { - log.error( - `Failed to get ${clientName}${ - targetPrincipal ? ` for ${targetPrincipal}` : "" - }: ${error.message}`, - { error, targetPrincipal }, - ); - throw error; - } -}; diff --git a/apps/event-worker/src/workers/resource-scan/google/cluster-to-resource.ts b/apps/event-worker/src/workers/resource-scan/google/cluster-to-resource.ts deleted file mode 100644 index 1066b0f4c..000000000 --- a/apps/event-worker/src/workers/resource-scan/google/cluster-to-resource.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type { KubernetesClusterAPIV1 } from "@ctrlplane/validators/resources"; -import type { google } from "@google-cloud/container/build/protos/protos.js"; -import { SemVer } from "semver"; - -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; -import { cloudRegionsGeo } from "@ctrlplane/validators/resources"; - -import { omitNullUndefined } from "../../../utils/omit-null-undefined.js"; - -export const clusterToResource = ( - workspaceId: string, - providerId: string, - project: string, - cluster: google.container.v1.ICluster, -): KubernetesClusterAPIV1 & { workspaceId: string; providerId: string } => { - const masterVersion = new SemVer(cluster.currentMasterVersion ?? "0"); - const nodeVersion = new SemVer(cluster.currentNodeVersion ?? "0"); - const autoscaling = String( - cluster.autoscaling?.enableNodeAutoprovisioning ?? false, - ); - const { timezone, latitude, longitude } = - cloudRegionsGeo[cluster.location ?? ""] ?? {}; - const appUrl = `https://console.cloud.google.com/kubernetes/clusters/details/${cluster.location}/${cluster.name}/details?project=${project}`; - return { - workspaceId, - name: cluster.name ?? cluster.id ?? "", - providerId, - identifier: `${project}/${cluster.name}`, - version: "kubernetes/v1", - kind: "ClusterAPI", - config: { - name: cluster.name!, - auth: { - method: "google/gke", - project, - location: cluster.location!, - clusterName: cluster.name!, - }, - status: cluster.status?.toString() ?? "STATUS_UNSPECIFIED", - server: { - certificateAuthorityData: cluster.masterAuth?.clusterCaCertificate, - endpoint: `https://${cluster.endpoint}`, - }, - }, - metadata: omitNullUndefined({ - [ReservedMetadataKey.Links]: JSON.stringify({ "Google Console": appUrl }), - [ReservedMetadataKey.ExternalId]: cluster.id ?? "", - [ReservedMetadataKey.LocationTimezone]: timezone, - [ReservedMetadataKey.LocationLatitude]: latitude, - [ReservedMetadataKey.LocationLongitude]: longitude, - - "google/self-link": cluster.selfLink, - "google/project": project, - "google/location": cluster.location, - "google/autopilot": String(cluster.autopilot?.enabled ?? false), - - [ReservedMetadataKey.KubernetesFlavor]: "gke", - [ReservedMetadataKey.KubernetesVersion]: - masterVersion.version.split("-")[0] ?? "", - [ReservedMetadataKey.KubernetesStatus]: - cluster.status === "RUNNING" - ? "running" - : cluster.status === "PROVISIONING" - ? "creating" - : "unknown", - "kubernetes/node-count": String(cluster.currentNodeCount ?? "unknown"), - - "kubernetes/master-version": masterVersion.version, - "kubernetes/master-version-major": String(masterVersion.major), - "kubernetes/master-version-minor": String(masterVersion.minor), - "kubernetes/master-version-patch": String(masterVersion.patch), - - "kubernetes/node-version": nodeVersion.version, - "kubernetes/node-version-major": String(nodeVersion.major), - "kubernetes/node-version-minor": String(nodeVersion.minor), - "kubernetes/node-version-patch": String(nodeVersion.patch), - - "kubernetes/autoscaling-enabled": autoscaling, - - ...(cluster.resourceLabels ?? {}), - }), - }; -}; diff --git a/apps/event-worker/src/workers/resource-scan/google/gke.ts b/apps/event-worker/src/workers/resource-scan/google/gke.ts deleted file mode 100644 index 782c54f14..000000000 --- a/apps/event-worker/src/workers/resource-scan/google/gke.ts +++ /dev/null @@ -1,216 +0,0 @@ -import type { - InsertResource, - ResourceProviderGoogle, - Workspace, -} from "@ctrlplane/db/schema"; -import type { ClusterManagerClient } from "@google-cloud/container"; -import type { google } from "@google-cloud/container/build/protos/protos.js"; -import type { KubeConfig } from "@kubernetes/client-node"; -import type { AuthClient } from "google-auth-library"; -import Container from "@google-cloud/container"; -import { CoreV1Api } from "@kubernetes/client-node"; -import _ from "lodash"; - -import { logger } from "@ctrlplane/logger"; - -import { getGoogleClient } from "./client.js"; -import { clusterToResource } from "./cluster-to-resource.js"; -import { createNamespaceResource, getKubeConfig } from "./kube.js"; -import { getVClustersForCluster } from "./vcluster.js"; - -const log = logger.child({ label: "resource-scan/gke" }); - -const getClusterClient = async ( - targetPrincipal?: string | null, -): Promise<[ClusterManagerClient, AuthClient | undefined]> => { - return getGoogleClient( - Container.v1.ClusterManagerClient, - targetPrincipal, - "GKE Cluster Client", - ); -}; - -const getClusters = async ( - clusterClient: ClusterManagerClient, - projectId: string, -) => { - const request = { parent: `projects/${projectId}/locations/-` }; - const [response] = await clusterClient.listClusters(request); - const { clusters } = response; - return clusters ?? []; -}; - -const getClustersByProject = async ( - googleClusterClient: ClusterManagerClient, - projectIds: string[], -) => { - const results = await Promise.allSettled( - projectIds.map(async (project) => - getClusters(googleClusterClient, project) - .then((clusters) => ({ project, clusters })) - .catch((e: any) => { - const isPermissionError = - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - e.message?.includes("PERMISSION_DENIED") || e.code === 403; - log.error( - `Unable to get clusters for project: ${project} - ${ - isPermissionError - ? 'Missing required permissions. Please ensure the service account has the "Service Account Token Creator" and "GKE Cluster Viewer" roles.' - : e.message - }`, - { error: e, project }, - ); - return { project, clusters: [] }; - }), - ), - ); - - return results - .filter( - ( - result, - ): result is PromiseFulfilledResult<{ - project: string; - clusters: google.container.v1.ICluster[]; - }> => result.status === "fulfilled", - ) - .map((v) => v.value); -}; - -const getNamespacesForCluster = async ( - kubeConfig: KubeConfig, - project: string, - cluster: google.container.v1.ICluster, - workspaceId: string, - resourceProviderId: string, -) => { - if (cluster.name == null || cluster.location == null) { - log.warn(`Skipping cluster with missing name or location`, { - project, - cluster, - workspaceId, - }); - return []; - } - - const k8sApi = kubeConfig.makeApiClient(CoreV1Api); - - try { - const response = await k8sApi.listNamespace(); - const namespaces = response.body.items; - const clusterResource = clusterToResource( - workspaceId, - resourceProviderId, - project, - cluster, - ); - - return namespaces - .filter((n) => n.metadata?.name != null) - .map((n) => - createNamespaceResource(clusterResource, n, project, cluster), - ); - } catch (error: any) { - log.error( - `Unable to list namespaces for cluster: ${cluster.name}/${cluster.id} - ${error.message}`, - { error, project, cluster, workspaceId }, - ); - return []; - } -}; - -export const getGkeResources = async ( - workspace: Workspace, - config: ResourceProviderGoogle, -) => { - const { googleServiceAccountEmail } = workspace; - log.info( - `Scanning ${config.projectIds.join(", ")} using ${googleServiceAccountEmail}`, - { workspaceId: workspace.id, config, googleServiceAccountEmail }, - ); - - const [googleClusterClient, impersonatedAuthClient] = await getClusterClient( - googleServiceAccountEmail, - ); - - const clusters = await getClustersByProject( - googleClusterClient, - config.projectIds, - ); - - const resources: InsertResource[] = []; - - if (config.importGke) - resources.push( - ...clusters.flatMap(({ project, clusters }) => - clusters.map((cluster) => - clusterToResource( - workspace.id, - config.resourceProviderId, - project, - cluster, - ), - ), - ), - ); - - const clustersWithProject = clusters - .map(({ project, clusters }) => - clusters.map((cluster) => ({ project, cluster })), - ) - .flat(); - - await Promise.all( - clustersWithProject.map(async ({ project, cluster }) => { - const kubeConfig = await getKubeConfig( - googleClusterClient, - impersonatedAuthClient, - project, - cluster.name!, - cluster.location!, - ).catch((e) => { - log.error( - `Failed to connect to cluster: ${cluster.name}/${cluster.id} - ${e.message}`, - { error: e, project, cluster, workspaceId: workspace.id }, - ); - return null; - }); - if (kubeConfig == null) return []; - - if (config.importNamespaces) - resources.push( - ...(await getNamespacesForCluster( - kubeConfig, - project, - cluster, - workspace.id, - config.resourceProviderId, - )), - ); - - if (config.importVCluster) - resources.push( - ...(await getVClustersForCluster( - kubeConfig, - project, - cluster, - workspace.id, - config.resourceProviderId, - )), - ); - - return resources; - }), - ); - - const resourceCounts = _.countBy(resources, (resource) => - [resource.kind, resource.version].join("/"), - ); - log.info(`Found ${resources.length} resources`, { - resourceCounts: Object.entries(resourceCounts) - .map(([key, count]) => `${key}: ${count}`) - .join(", "), - }); - - return resources; -}; diff --git a/apps/event-worker/src/workers/resource-scan/google/kube.ts b/apps/event-worker/src/workers/resource-scan/google/kube.ts deleted file mode 100644 index 3974e5352..000000000 --- a/apps/event-worker/src/workers/resource-scan/google/kube.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { InsertResource } from "@ctrlplane/db/schema"; -import type { ClusterManagerClient } from "@google-cloud/container"; -import type { google } from "@google-cloud/container/build/protos/protos.js"; -import type { AuthClient } from "google-auth-library"; -import { KubeConfig } from "@kubernetes/client-node"; -import _ from "lodash"; - -import { sourceCredentials } from "./client.js"; - -export const getKubeConfig = async ( - clusterClient: ClusterManagerClient, - authClient: AuthClient | undefined, - project: string, - clusterName: string, - clusterLocation: string, -) => { - const [credentials] = await clusterClient.getCluster({ - name: `projects/${project}/locations/${clusterLocation}/clusters/${clusterName}`, - }); - - const token = await (authClient != null - ? authClient.getAccessToken().then((t) => t.token) - : sourceCredentials.getAccessToken()); - if (token == null) throw new Error("Unable to get kubernetes access token."); - - const kubeConfig = new KubeConfig(); - kubeConfig.loadFromOptions({ - clusters: [ - { - name: clusterName, - server: `https://${credentials.endpoint}`, - caData: credentials.masterAuth!.clusterCaCertificate!, - }, - ], - users: [{ name: clusterName, token }], - contexts: [ - { - name: clusterName, - user: clusterName, - cluster: clusterName, - }, - ], - currentContext: clusterName, - }); - return kubeConfig; -}; - -type Namespace = { - metadata?: { - name?: string; - labels?: Record; - }; -}; - -export const createNamespaceResource = ( - clusterResource: InsertResource, - namespace: Namespace, - project: string, - cluster: google.container.v1.ICluster, -) => { - return _.merge(_.cloneDeep(clusterResource), { - name: `${cluster.name ?? cluster.id ?? ""}/${namespace.metadata!.name}`, - kind: "Namespace", - identifier: `${project}/${cluster.name}/${namespace.metadata!.name}`, - config: { namespace: namespace.metadata!.name }, - metadata: { - ...namespace.metadata?.labels, - "kubernetes/namespace": namespace.metadata!.name ?? "", - }, - }); -}; diff --git a/apps/event-worker/src/workers/resource-scan/google/vcluster.ts b/apps/event-worker/src/workers/resource-scan/google/vcluster.ts deleted file mode 100644 index c13e7aadf..000000000 --- a/apps/event-worker/src/workers/resource-scan/google/vcluster.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { exec as execCallback } from "node:child_process"; -import fs from "node:fs"; -import { promisify } from "node:util"; -import type { google } from "@google-cloud/container/build/protos/protos"; -import type { KubeConfig } from "@kubernetes/client-node"; -import { SemVer } from "semver"; -import { v4 as uuidv4 } from "uuid"; - -import { logger } from "@ctrlplane/logger"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; - -import { clusterToResource } from "./cluster-to-resource.js"; - -const log = logger.child({ module: "resource-scan/gke/vcluster" }); - -const exec = promisify(execCallback); - -export const getVClustersForCluster = async ( - kubeConfig: KubeConfig, - project: string, - cluster: google.container.v1.ICluster, - workspaceId: string, - resourceProviderId: string, -) => { - if (cluster.name == null || cluster.location == null) { - log.warn(`Skipping cluster with missing name or location`, { - project, - cluster, - workspaceId, - }); - return []; - } - - const kubeconfigPath = `/tmp/kubeconfig-${uuidv4()}`; - try { - await fs.promises.writeFile(kubeconfigPath, kubeConfig.exportConfig()); - const { stdout } = await exec( - `KUBECONFIG=${kubeconfigPath} vcluster list --output=json`, - ); - await fs.promises.unlink(kubeconfigPath); - - const vclusters = JSON.parse(stdout) as Array<{ - Name: string; - Namespace: string; - Status: string; - Created: string; - Version: string; - }> | null; - - if (vclusters == null) { - log.info( - `No vclusters found for cluster: ${cluster.name}/${cluster.id}`, - { project, clusterName: cluster.name, workspaceId }, - ); - return []; - } - - const clusterResource = clusterToResource( - workspaceId, - resourceProviderId, - project, - cluster, - ); - - return vclusters.map((vcluster) => { - const version = new SemVer(vcluster.Version); - return { - ...clusterResource, - name: `${cluster.name}/${vcluster.Namespace}/${vcluster.Name}`, - identifier: `${project}/${cluster.name}/${vcluster.Namespace}/${vcluster.Name}`, - kind: "ClusterAPI", - config: { - ...clusterResource.config, - name: cluster.name, - namespace: vcluster.Namespace, - status: vcluster.Status, - vcluster: vcluster.Name, - }, - metadata: { - ...clusterResource.metadata, - "vcluster/version": vcluster.Version, - "vcluster/version-major": String(version.major), - "vcluster/version-minor": String(version.minor), - "vcluster/version-patch": String(version.patch), - "vcluster/name": vcluster.Name, - "vcluster/namespace": vcluster.Namespace, - "vcluster/status": vcluster.Status, - "vcluster/created": vcluster.Created, - [ReservedMetadataKey.KubernetesFlavor]: "vcluster", - }, - }; - }); - } catch (error: any) { - log.error( - `Unable to list vclusters for cluster: ${cluster.name}/${cluster.id} - ${error.message}`, - { error, project, cluster, workspaceId }, - ); - return []; - } -}; diff --git a/apps/event-worker/src/workers/resource-scan/google/vm.ts b/apps/event-worker/src/workers/resource-scan/google/vm.ts deleted file mode 100644 index 031ffabbc..000000000 --- a/apps/event-worker/src/workers/resource-scan/google/vm.ts +++ /dev/null @@ -1,191 +0,0 @@ -import type * as SCHEMA from "@ctrlplane/db/schema"; -import type { InstanceV1 } from "@ctrlplane/validators/resources"; -import type { google } from "@google-cloud/compute/build/protos/protos.js"; -import { InstancesClient } from "@google-cloud/compute"; -import _ from "lodash"; - -import { logger } from "@ctrlplane/logger"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; - -import { omitNullUndefined } from "../../../utils/omit-null-undefined.js"; -import { getGoogleClient } from "./client.js"; - -const log = logger.child({ module: "resource-scan/gke/vm" }); - -const getVMClient = (targetPrincipal?: string | null) => - getGoogleClient(InstancesClient, targetPrincipal, "VM Client"); - -const getFlattenedMetadata = (metadata?: google.cloud.compute.v1.IMetadata) => { - if (metadata == null) return {}; - const { items } = metadata; - return _.fromPairs([ - ...(items?.map(({ key, value }) => [`compute/label/${key}`, value ?? ""]) ?? - []), - ...(items?.map(({ key, value }) => [`google/label/${key}`, value ?? ""]) ?? - []), - ]); -}; - -const getFlattenedTags = (tags?: google.cloud.compute.v1.ITags) => { - if (tags == null) return {}; - const { items } = tags; - return _.fromPairs([ - ...(items?.map((value) => [`compute/tag/${value}`, "true"]) ?? []), - ...(items?.map((value) => [`google/tag/${value}`, "true"]) ?? []), - ]); -}; - -const instanceToResource = ( - instance: google.cloud.compute.v1.IInstance, - workspaceId: string, - providerId: string, - projectId: string, -): InstanceV1 => { - const instanceZone = - instance.zone != null ? instance.zone.split("/").pop() : null; - const appUrl = `https://console.cloud.google.com/compute/instancesDetail/zones/${instanceZone}/instances/${instance.name}?project=${projectId}`; - return { - workspaceId, - name: String(instance.name ?? instance.id ?? ""), - providerId, - identifier: `${projectId}/${instance.name}`, - version: "compute/v1", - kind: "Instance", - config: { - name: String(instance.name ?? instance.id ?? ""), - id: String(instance.id ?? ""), - connectionMethod: { - type: "gcp", - project: projectId, - instanceName: String(instance.name ?? instance.id ?? ""), - zone: instanceZone ?? "", - username: instance.serviceAccounts?.[0]?.email ?? "", - }, - }, - metadata: omitNullUndefined({ - "compute/machine-type": instance.machineType?.split("/").pop(), - "compute/type": instance.machineType?.toLowerCase().includes("compute") - ? "compute" - : instance.machineType?.toLowerCase().includes("memory") || - instance.machineType?.toLowerCase().includes("highmem") - ? "memory" - : instance.machineType?.toLowerCase().includes("storage") - ? "storage" - : instance.machineType?.toLowerCase().includes("gpu") || - instance.machineType?.toLowerCase().includes("tpu") - ? "accelerated" - : "standard", - - [ReservedMetadataKey.Links]: JSON.stringify({ "Google Console": appUrl }), - - [ReservedMetadataKey.ExternalId]: instance.id ?? "", - "google/self-link": instance.selfLink, - "google/project": projectId, - "google/zone": instance.zone, - "compute/can-ip-forward": instance.canIpForward, - "compute/cpu-platform": instance.cpuPlatform, - "compute/deletion-protection": instance.deletionProtection, - "compute/description": instance.description, - "compute/status": instance.status, - "compute/disk-count": instance.disks?.length ?? 0, - "compute/disk-size-gb": _.sumBy(instance.disks, (disk) => - disk.diskSizeGb != null ? Number(disk.diskSizeGb) : 0, - ), - "compute/disk-type": - instance.disks?.map((disk) => disk.type).join(", ") ?? "", - "compute/instance-status": instance.status, - "compute/fingerprint": instance.fingerprint, - "compute/hostname": instance.hostname, - "compute/instance-encryption-key": instance.instanceEncryptionKey, - "compute/key-revocation-action-type": instance.keyRevocationActionType, - "compute/kind": instance.kind, - "compute/last-start-timestamp": instance.lastStartTimestamp, - "compute/last-stop-timestamp": instance.lastStopTimestamp, - "compute/last-suspended-timestamp": instance.lastSuspendedTimestamp, - ...getFlattenedMetadata(instance.metadata ?? undefined), - "compute/min-cpu-platform": instance.minCpuPlatform, - "compute/network-performance-config/total-egress-bandwith-tier": - instance.networkPerformanceConfig?.totalEgressBandwidthTier, - "compute/private-ipv6-google-access": instance.privateIpv6GoogleAccess, - "compute/reservation-affinity/consume-reservation-type": - instance.reservationAffinity?.consumeReservationType, - "compute/resource-policies": - instance.resourcePolicies?.join(", ") ?? null, - "compute/scheduling/automatic-restart": - instance.scheduling?.automaticRestart, - "compute/scheduling/availability-domain": - instance.scheduling?.availabilityDomain, - "compute/scheduling/instance-termination-action": - instance.scheduling?.instanceTerminationAction, - "compute/scheduling/local-ssd-recovery-timeout": - instance.scheduling?.localSsdRecoveryTimeout, - "compute/scheduling/location-hint": instance.scheduling?.locationHint, - "compute/scheduling/max-run-duration": - instance.scheduling?.maxRunDuration, - "compute/scheduling/min-node-cpus": instance.scheduling?.minNodeCpus, - "compute/scheduling/node-affinities": - instance.scheduling?.nodeAffinities - ?.map((affinity) => affinity.key) - .join(", ") ?? null, - "compute/scheduling/on-host-maintenance": - instance.scheduling?.onHostMaintenance, - "compute/scheduling/on-instance-stop-action": - instance.scheduling?.onInstanceStopAction, - "compute/scheduling/preemptible": instance.scheduling?.preemptible, - "compute/scheduling/provisioning-model": - instance.scheduling?.provisioningModel, - "compute/scheduling/termination-time": - instance.scheduling?.terminationTime, - "compute/service-accounts": - instance.serviceAccounts?.map((account) => account.email).join(", ") ?? - null, - ...instance.labels, - ...getFlattenedTags(instance.tags ?? undefined), - }), - }; -}; - -export const getGoogleVMResources = async ( - workspace: SCHEMA.Workspace, - config: SCHEMA.ResourceProviderGoogle, -): Promise => { - if (!config.importVms) return []; - const { googleServiceAccountEmail } = workspace; - const [vmClient] = await getVMClient(googleServiceAccountEmail); - log.info( - `Scanning VMs for ${config.projectIds.join(", ")} using ${googleServiceAccountEmail}`, - { workspaceId: workspace.id, config, googleServiceAccountEmail }, - ); - - return Promise.all( - config.projectIds.map(async (projectId) => { - try { - const allResources: SCHEMA.InsertResource[] = []; - for await (const [_, instances] of vmClient.aggregatedListAsync({ - project: projectId, - returnPartialSuccess: true, - })) { - if (instances.instances == null || instances.instances.length === 0) - continue; - const resources = instances.instances.map((instance) => - instanceToResource( - instance, - workspace.id, - config.resourceProviderId, - projectId, - ), - ); - allResources.push(...resources); - } - - return allResources; - } catch (error: any) { - log.error( - `Unable to list VMs for provider ${config.id} and project ${projectId}: ${error.message}`, - { error, projectId, providerId: config.resourceProviderId }, - ); - return []; - } - }), - ).then((results) => results.flat()); -}; diff --git a/apps/event-worker/src/workers/resource-scan/google/vpc.ts b/apps/event-worker/src/workers/resource-scan/google/vpc.ts deleted file mode 100644 index 056433ad6..000000000 --- a/apps/event-worker/src/workers/resource-scan/google/vpc.ts +++ /dev/null @@ -1,193 +0,0 @@ -import type { - InsertResource, - ResourceProviderGoogle, - Workspace, -} from "@ctrlplane/db/schema"; -import type { - CloudSubnetV1, - CloudVPCV1, -} from "@ctrlplane/validators/resources"; -import type { google } from "@google-cloud/compute/build/protos/protos.js"; -import { NetworksClient, SubnetworksClient } from "@google-cloud/compute"; -import _ from "lodash"; -import { isPresent } from "ts-is-present"; - -import { logger } from "@ctrlplane/logger"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; - -import { omitNullUndefined } from "../../../utils/omit-null-undefined.js"; -import { getGoogleClient } from "./client.js"; - -const log = logger.child({ label: "resource-scan/google/vpc" }); - -const getNetworksClient = async (targetPrincipal?: string | null) => - Promise.all([ - getGoogleClient(NetworksClient, targetPrincipal, "Networks Client"), - getGoogleClient(SubnetworksClient, targetPrincipal, "Subnets Client"), - ]).then(([[networksClient], [subnetsClient]]) => ({ - networksClient, - subnetsClient, - })); - -const getSubnetDetails = ( - subnetsClient: SubnetworksClient, - project: string, - subnetSelfLink: string, -): Promise => { - const parts = subnetSelfLink.split("/"); - const region = parts.at(-3) ?? ""; - const name = parts.at(-1) ?? ""; - - return subnetsClient - .list({ - project, - region, - filter: `name eq ${name}`, - }) - .then(([subnets]) => { - const subnet = subnets.find((subnet) => subnet.name === name); - if (subnet === undefined) return null; - - return { - name, - region, - gatewayAddress: subnet.gatewayAddress ?? "", - cidr: subnet.ipCidrRange ?? "", - type: subnet.purpose === "INTERNAL" ? "internal" : "external", - secondaryCidrs: subnet.secondaryIpRanges?.map((r) => ({ - name: r.rangeName ?? "", - cidr: r.ipCidrRange ?? "", - })), - }; - }); -}; - -const getNetworkResources = async ( - clients: { networksClient: NetworksClient; subnetsClient: SubnetworksClient }, - project: string, - networks: google.cloud.compute.v1.INetwork[], -): Promise => - await Promise.all( - networks - .filter((n) => n.name != null) - .map(async (network) => { - const subnets = await Promise.all( - (network.subnetworks ?? []).map((subnet) => - getSubnetDetails(clients.subnetsClient, project, subnet), - ), - ); - return { - name: network.name!, - identifier: `${project}/${network.name}`, - version: "cloud/v1", - kind: "VPC", - config: { - name: network.name!, - provider: "google", - region: "global", - project, - cidr: network.IPv4Range ?? undefined, - mtu: network.mtu ?? undefined, - subnets: subnets.filter(isPresent), - }, - metadata: omitNullUndefined({ - [ReservedMetadataKey.ExternalId]: network.id?.toString(), - [ReservedMetadataKey.Links]: JSON.stringify({ - "Google Console": `https://console.cloud.google.com/networking/networks/details/${network.name}?project=${project}`, - }), - "google/project": project, - "google/self-link": network.selfLink, - "google/creation-timestamp": network.creationTimestamp, - "google/description": network.description, - ...(network.peerings - ? Object.fromEntries( - network.peerings.flatMap((peering) => [ - [`google/peering/${peering.name}/network`, peering.network], - [`google/peering/${peering.name}/state`, peering.state], - [ - `google/peering/${peering.name}/auto-create-routes`, - peering.autoCreateRoutes?.toString() ?? "false", - ], - ]), - ) - : {}), - }), - }; - }), - ); - -const fetchProjectNetworks = async ( - clients: { networksClient: NetworksClient; subnetsClient: SubnetworksClient }, - project: string, - workspaceId: string, - providerId: string, -): Promise => { - try { - const networks: InsertResource[] = []; - let pageToken: string | undefined | null; - - do { - const [networkList, request] = await clients.networksClient.list({ - project, - maxResults: 500, - pageToken, - }); - - const resources = await getNetworkResources( - clients, - project, - networkList, - ); - - networks.push( - ...resources.map((resource) => ({ - ...resource, - workspaceId, - providerId, - })), - ); - pageToken = request?.pageToken ?? null; - } while (pageToken != null); - - return networks; - } catch (err) { - const { message, code } = err as { message?: string; code?: number }; - const errorMessage = - message?.includes("PERMISSION_DENIED") || code === 403 - ? 'Missing required permissions. Please ensure the service account has the "Compute Network Viewer" role.' - : (message ?? "Unknown error"); - - log.error(`Unable to get VPCs for project: ${project} - ${errorMessage}`, { - error: err, - project, - }); - return []; - } -}; - -export const getVpcResources = async ( - workspace: Workspace, - config: ResourceProviderGoogle, -) => { - const { googleServiceAccountEmail, id: workspaceId } = workspace; - const { resourceProviderId } = config; - log.info( - `Scanning VPCs in ${config.projectIds.join(", ")} using ${googleServiceAccountEmail}`, - { workspaceId, config, googleServiceAccountEmail, resourceProviderId }, - ); - - const clients = await getNetworksClient(googleServiceAccountEmail); - const resources: InsertResource[] = config.importVpc - ? await _.chain(config.projectIds) - .map((id) => - fetchProjectNetworks(clients, id, workspaceId, resourceProviderId), - ) - .thru((promises) => Promise.all(promises)) - .value() - .then((results) => results.flat()) - : []; - - log.info(`Found ${resources.length} VPC resources`); - - return resources; -}; diff --git a/apps/event-worker/src/workers/resource-scan/index.ts b/apps/event-worker/src/workers/resource-scan/index.ts deleted file mode 100644 index f62bd50fc..000000000 --- a/apps/event-worker/src/workers/resource-scan/index.ts +++ /dev/null @@ -1,120 +0,0 @@ -import type { ResourceToInsert } from "@ctrlplane/events"; -import type { Job } from "bullmq"; - -import { eq, takeFirstOrNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import { - resourceProvider, - resourceProviderAws, - resourceProviderAzure, - resourceProviderGoogle, - workspace, -} from "@ctrlplane/db/schema"; -import { - Channel, - createWorker, - getQueue, - handleResourceProviderScan, -} from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; - -import { getEksResources } from "./aws/eks.js"; -import { getVpcResources as getAwsVpcResources } from "./aws/vpc.js"; -import { getAksResources } from "./azure/aks.js"; -import { getGkeResources } from "./google/gke.js"; -import { getGoogleVMResources } from "./google/vm.js"; -import { getVpcResources as getGoogleVpcResources } from "./google/vpc.js"; -import { extractVariablesFromMetadata } from "./utils/extract-variables.js"; - -const log = logger.child({ label: "resource-scan" }); - -const removeResourceJob = (job: Job) => - job.repeatJobKey != null - ? getQueue(Channel.ResourceScan).removeRepeatableByKey(job.repeatJobKey) - : null; - -const getResources = async (rp: any): Promise => { - if (rp.resource_provider_google != null) { - const [gkeResources, vpcResources, vmResources] = await Promise.all([ - getGkeResources(rp.workspace, rp.resource_provider_google), - getGoogleVpcResources(rp.workspace, rp.resource_provider_google), - getGoogleVMResources(rp.workspace, rp.resource_provider_google), - ]); - return [...gkeResources, ...vpcResources, ...vmResources]; - } - - if (rp.resource_provider_aws != null) { - const [eksResources, vpcResources] = await Promise.all([ - getEksResources(rp.workspace, rp.resource_provider_aws), - getAwsVpcResources(rp.workspace, rp.resource_provider_aws), - ]); - return [...eksResources, ...vpcResources]; - } - - if (rp.resource_provider_azure != null) - return getAksResources(rp.workspace, rp.resource_provider_azure); - throw new Error("Invalid resource provider"); -}; - -export const resourceScanWorker = createWorker( - Channel.ResourceScan, - async (job) => { - const { resourceProviderId } = job.data; - - const rp = await db - .select() - .from(resourceProvider) - .where(eq(resourceProvider.id, resourceProviderId)) - .innerJoin(workspace, eq(resourceProvider.workspaceId, workspace.id)) - .leftJoin( - resourceProviderGoogle, - eq(resourceProvider.id, resourceProviderGoogle.resourceProviderId), - ) - .leftJoin( - resourceProviderAws, - eq(resourceProvider.id, resourceProviderAws.resourceProviderId), - ) - .leftJoin( - resourceProviderAzure, - eq(resourceProvider.id, resourceProviderAzure.resourceProviderId), - ) - .then(takeFirstOrNull); - - if (rp == null) { - log.error(`Resource provider with ID ${resourceProviderId} not found.`); - await removeResourceJob(job); - return; - } - - log.info( - `Received scanning request for "${rp.resource_provider.name}" (${resourceProviderId}).`, - ); - - try { - const resources = await getResources(rp); - if (resources.length === 0) { - log.info( - `No resources found for provider ${rp.resource_provider.id}, skipping upsert.`, - ); - return; - } - - const resourcesWithVariables = extractVariablesFromMetadata(resources); - - log.info( - `Upserting ${resourcesWithVariables.length} resources for provider ${rp.resource_provider.id}`, - ); - await handleResourceProviderScan( - db, - rp.workspace.id, - rp.resource_provider.id, - resourcesWithVariables, - ); - } catch (error: any) { - log.error( - `Error scanning/upserting resources for provider ${rp.resource_provider.id}: ${error.message}`, - { error }, - ); - } - }, -); diff --git a/apps/event-worker/src/workers/resource-scan/utils/extract-variables.ts b/apps/event-worker/src/workers/resource-scan/utils/extract-variables.ts deleted file mode 100644 index 237c4510c..000000000 --- a/apps/event-worker/src/workers/resource-scan/utils/extract-variables.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { ResourceToInsert } from "@ctrlplane/events"; - -const convertToTypedValue = ( - stringValue: string, -): string | number | boolean => { - if (stringValue === "true") return true; - if (stringValue === "false") return false; - const numValue = Number(stringValue); - if (stringValue.trim() !== "" && !isNaN(numValue)) return numValue; - return stringValue; -}; - -/** - * Extracts resource variables from metadata keys prefixed with "variable-" - * Works with metadata from any provider (Google labels, AWS tags, Azure tags, etc.) - * @param resources Array of resources to process - * @returns Array of resources with variables extracted from metadata - */ -export const extractVariablesFromMetadata = ( - resources: ResourceToInsert[], -): ResourceToInsert[] => { - return resources.map((resource) => { - const variables = Object.entries(resource.metadata ?? {}) - .filter( - ([key]) => - key.startsWith("variable-") && key.length > "variable-".length, - ) - .map(([key, rawValue]) => ({ - key: key.slice("variable-".length), - value: convertToTypedValue(String(rawValue)), - sensitive: false, - })); - - return { ...resource, variables }; - }); -}; diff --git a/apps/event-worker/src/workers/update-deployment-variable.ts b/apps/event-worker/src/workers/update-deployment-variable.ts deleted file mode 100644 index a6b71ab4c..000000000 --- a/apps/event-worker/src/workers/update-deployment-variable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { eq } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -/** - * Worker that handles deployment variable changes - * - * When a deployment variable is updated, perform the following steps: - * 1. Grab all release targe†s associated with the deployment - * 2. Add them to the evaluation queue - * - * @param {Job} job - The deployment variable data - * @returns {Promise} A promise that resolves when processing is complete - */ -export const updateDeploymentVariableWorker = createWorker( - Channel.UpdateDeploymentVariable, - async (job) => { - const variable = await db.query.deploymentVariable.findFirst({ - where: eq(schema.deploymentVariable.id, job.data.id), - }); - - if (variable == null) throw new Error("Deployment variable not found"); - - const releaseTargets = await db.query.releaseTarget.findMany({ - where: eq(schema.releaseTarget.deploymentId, variable.deploymentId), - }); - - await dispatchQueueJob().toEvaluate().releaseTargets(releaseTargets); - }, -); diff --git a/apps/event-worker/src/workers/update-deployment.ts b/apps/event-worker/src/workers/update-deployment.ts deleted file mode 100644 index 3a4a9c0b4..000000000 --- a/apps/event-worker/src/workers/update-deployment.ts +++ /dev/null @@ -1,51 +0,0 @@ -import _ from "lodash"; - -import { eq } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; - -const log = logger.child({ module: "update-deployment" }); - -/** - * Worker that does the post-processing after a deployment is updated - * 1. Grab the current release targets for the deployment, with resources - * 2. For the current resources, recompute the release targets - * - * @param {Job} job - The deployment data - * @returns {Promise} A promise that resolves when processing is complete - */ -export const updateDeploymentWorker = createWorker( - Channel.UpdateDeployment, - async ({ data }) => { - try { - const jobAgentChanged = !_.isEqual( - data.old.jobAgentId, - data.new.jobAgentId, - ); - const jobAgentConfigChanged = !_.isEqual( - data.old.jobAgentConfig, - data.new.jobAgentConfig, - ); - if (jobAgentChanged || jobAgentConfigChanged) { - const releaseTargets = await db.query.releaseTarget.findMany({ - where: eq(schema.releaseTarget.deploymentId, data.new.id), - }); - - await dispatchQueueJob().toEvaluate().releaseTargets(releaseTargets); - } - - if (_.isEqual(data.old.resourceSelector, data.new.resourceSelector)) - return; - - await dispatchQueueJob() - .toCompute() - .deployment(data.new) - .resourceSelector(); - } catch (error) { - log.error("Error updating deployment", { error }); - throw error; - } - }, -); diff --git a/apps/event-worker/src/workers/update-environment.ts b/apps/event-worker/src/workers/update-environment.ts deleted file mode 100644 index 800519f70..000000000 --- a/apps/event-worker/src/workers/update-environment.ts +++ /dev/null @@ -1,41 +0,0 @@ -import _ from "lodash"; - -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; - -const log = logger.child({ - module: "env-selector-update", - function: "envSelectorUpdateWorker", -}); - -/** - * Worker that handles environment updates. - * - * When an environment is updated and the resource selector is changed, perform the following steps: - * 1. Recompute the resources for the environment and return which resources - * have been added and which have been removed - * 2. For all affected resources, replace the release targets based on new computations - * 3. Recompute all policy targets' computed release targets based on the new release targets - * 4. Add all replaced release targets to the evaluation queue - * 5. Dispatch exit hooks for the exited resources - * - * @param {Job} job - The job containing environment data with old and new selectors - * @returns {Promise} - Resolves when processing is complete - * @throws {Error} - If there's an issue with database operations - */ -export const updateEnvironmentWorker = createWorker( - Channel.UpdateEnvironment, - async (job) => { - try { - const { oldSelector, resourceSelector } = job.data; - if (_.isEqual(oldSelector, resourceSelector)) return; - await dispatchQueueJob() - .toCompute() - .environment(job.data) - .resourceSelector(); - } catch (error) { - log.error("Error updating environment", { error }); - throw error; - } - }, -); diff --git a/apps/event-worker/src/workers/update-policy.ts b/apps/event-worker/src/workers/update-policy.ts deleted file mode 100644 index 7dfe9e623..000000000 --- a/apps/event-worker/src/workers/update-policy.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { eq } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -export const updatePolicyWorker = createWorker( - Channel.UpdatePolicy, - async (job) => { - const policyTargets = await db.query.policyTarget.findMany({ - where: eq(schema.policyTarget.policyId, job.data.id), - }); - - for (const policyTarget of policyTargets) - dispatchQueueJob() - .toCompute() - .policyTarget(policyTarget) - .releaseTargetSelector(); - }, -); diff --git a/apps/event-worker/src/workers/update-resource-variable.ts b/apps/event-worker/src/workers/update-resource-variable.ts deleted file mode 100644 index c9b3c88bb..000000000 --- a/apps/event-worker/src/workers/update-resource-variable.ts +++ /dev/null @@ -1,63 +0,0 @@ -import _ from "lodash"; - -import { and, eq, inArray } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import { getResourceChildren } from "@ctrlplane/db/queries"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -const getResourceReleaseTargets = async (resourceId: string) => - db.query.releaseTarget.findMany({ - where: eq(schema.releaseTarget.resourceId, resourceId), - }); - -const getResourceChildrenReleaseTargets = async ( - resourceId: string, - key: string, -) => { - const dependentResources = await getResourceChildren(db, resourceId); - - return db - .selectDistinctOn([schema.releaseTarget.id]) - .from(schema.releaseTarget) - .innerJoin( - schema.deployment, - eq(schema.releaseTarget.deploymentId, schema.deployment.id), - ) - .innerJoin( - schema.deploymentVariable, - eq(schema.deploymentVariable.deploymentId, schema.deployment.id), - ) - .where( - and( - eq(schema.deploymentVariable.key, key), - inArray( - schema.releaseTarget.resourceId, - dependentResources.map((dr) => dr.target.id), - ), - ), - ) - .then((rows) => rows.map((row) => row.release_target)); -}; - -export const updateResourceVariableWorker = createWorker( - Channel.UpdateResourceVariable, - async (job) => { - const { data: variable } = job; - const { resourceId, key } = variable; - - const resourceReleaseTargets = await getResourceReleaseTargets(resourceId); - - const childrenReleaseTargets = await getResourceChildrenReleaseTargets( - resourceId, - key, - ); - - const affectedReleaseTargets = [ - ...resourceReleaseTargets, - ...childrenReleaseTargets, - ]; - - dispatchQueueJob().toEvaluate().releaseTargets(affectedReleaseTargets); - }, -); diff --git a/apps/event-worker/src/workers/updated-resources/index.ts b/apps/event-worker/src/workers/updated-resources/index.ts deleted file mode 100644 index b0778e351..000000000 --- a/apps/event-worker/src/workers/updated-resources/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -import _ from "lodash"; - -import { and, eq, inArray } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import { getResourceChildren } from "@ctrlplane/db/queries"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, createWorker, dispatchQueueJob } from "@ctrlplane/events"; - -import { createSpanWrapper } from "./span.js"; - -const getAffectedReleaseTargets = async (resourceId: string) => { - const resourceChildren = await getResourceChildren(db, resourceId); - const releaseTargets = await db - .selectDistinctOn([schema.releaseTarget.id]) - .from(schema.releaseTarget) - .where( - and( - inArray( - schema.releaseTarget.resourceId, - resourceChildren.map((dr) => dr.target.id), - ), - ), - ); - - return releaseTargets; -}; - -export const updatedResourceWorker = createWorker( - Channel.UpdatedResource, - createSpanWrapper( - "updatedResourceWorker", - async (span, { data: resource }) => { - span.setAttribute("resource.id", resource.id); - span.setAttribute("resource.name", resource.name); - span.setAttribute("workspace.id", resource.workspaceId); - - const workspace = await db.query.workspace.findFirst({ - where: eq(schema.workspace.id, resource.workspaceId), - with: { systems: { with: { environments: true, deployments: true } } }, - }); - - if (workspace == null) throw new Error("Workspace not found"); - - const deployments = workspace.systems.flatMap( - ({ deployments }) => deployments, - ); - - const environments = workspace.systems.flatMap( - ({ environments }) => environments, - ); - - for (const deployment of deployments) - await dispatchQueueJob() - .toCompute() - .deployment(deployment) - .resourceSelector(); - - for (const environment of environments) - await dispatchQueueJob() - .toCompute() - .environment(environment) - .resourceSelector(); - - const affectedReleaseTargets = await getAffectedReleaseTargets( - resource.id, - ); - await dispatchQueueJob() - .toEvaluate() - .releaseTargets(affectedReleaseTargets); - }, - ), - { concurrency: 25 }, -); diff --git a/apps/event-worker/src/workers/updated-resources/span.ts b/apps/event-worker/src/workers/updated-resources/span.ts deleted file mode 100644 index 14aff3b49..000000000 --- a/apps/event-worker/src/workers/updated-resources/span.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { makeWithSpan, trace } from "@ctrlplane/logger"; - -const tracer = trace.getTracer("updated-resource"); -export const { createSpanWrapper } = makeWithSpan(tracer); diff --git a/apps/event-worker/tsconfig.json b/apps/event-worker/tsconfig.json deleted file mode 100644 index 34755271a..000000000 --- a/apps/event-worker/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "@ctrlplane/tsconfig/base.json", - "compilerOptions": { - "target": "ESNext", - "outDir": "dist", - "noEmit": false, - "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json", - "baseUrl": ".", - "esModuleInterop": true, - "importsNotUsedAsValues": "remove" - }, - "include": ["src", "*.ts"], - "exclude": ["node_modules"] -} diff --git a/apps/webservice/src/app/api/azure/[workspaceId]/[tenantId]/[subscriptionId]/[name]/route.ts b/apps/webservice/src/app/api/azure/[workspaceId]/[tenantId]/[subscriptionId]/[name]/route.ts deleted file mode 100644 index 1f353df3f..000000000 --- a/apps/webservice/src/app/api/azure/[workspaceId]/[tenantId]/[subscriptionId]/[name]/route.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { randomUUID } from "crypto"; -import type { Tx } from "@ctrlplane/db"; -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { FORBIDDEN, INTERNAL_SERVER_ERROR, NOT_FOUND } from "http-status"; -import ms from "ms"; - -import { redis } from "@ctrlplane/api"; -import { eq, takeFirstOrNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as SCHEMA from "@ctrlplane/db/schema"; -import { Channel, getQueue } from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; - -import { urls } from "~/app/urls"; -import { env } from "~/env"; - -type Params = { - workspaceId: string; - tenantId: string; - subscriptionId: string; - name: string; -}; - -const baseUrl = env.BASE_URL; -const clientId = env.AZURE_APP_CLIENT_ID; - -const resourceScanQueue = getQueue(Channel.ResourceScan); - -const createResourceProvider = async ( - db: Tx, - workspaceId: string, - tenantId: string, - subscriptionId: string, - name: string, -) => { - const resourceProvider = await db - .insert(SCHEMA.resourceProvider) - .values({ workspaceId, name }) - .returning() - .then(takeFirstOrNull); - - if (resourceProvider == null) - throw new Error("Failed to create resource provider"); - - await db.insert(SCHEMA.resourceProviderAzure).values({ - resourceProviderId: resourceProvider.id, - tenantId, - subscriptionId, - }); - - await resourceScanQueue.add( - resourceProvider.id, - { resourceProviderId: resourceProvider.id }, - { repeat: { every: ms("10m"), immediately: true } }, - ); - - return resourceProvider; -}; - -export const GET = async ( - request: NextRequest, - props: { params: Promise }, -) => { - const params = await props.params; - const { workspaceId, tenantId, subscriptionId, name } = params; - const { searchParams } = new URL(request.url); - const resourceProviderId = searchParams.get("resourceProviderId"); - - return db.transaction(async (db) => { - const workspace = await db - .select() - .from(SCHEMA.workspace) - .where(eq(SCHEMA.workspace.id, workspaceId)) - .then(takeFirstOrNull); - - if (workspace == null) - return NextResponse.json( - { error: "Workspace not found" }, - { status: NOT_FOUND }, - ); - - const tenant = await db - .select() - .from(SCHEMA.azureTenant) - .where(eq(SCHEMA.azureTenant.tenantId, tenantId)) - .then(takeFirstOrNull); - - if (tenant == null) { - const state = randomUUID(); - const config = { workspaceId, tenantId, subscriptionId, name }; - const configJSON = JSON.stringify(config); - await redis.set(`azure_consent_state:${state}`, configJSON, "EX", 900); - const redirectUrl = `${baseUrl}/api/azure/consent`; - const consentUrlExtension = - resourceProviderId == null - ? "" - : `&resourceProviderId=${resourceProviderId}`; - const consentUrl = `https://login.microsoftonline.com/${tenantId}/adminconsent?client_id=${clientId}&redirect_uri=${redirectUrl}&state=${state}${consentUrlExtension}`; - return NextResponse.redirect(consentUrl); - } - - if (tenant.workspaceId !== workspaceId) - return NextResponse.json( - { error: "Tenant does not belong to this workspace" }, - { status: FORBIDDEN }, - ); - - const azureUrl = urls - .workspace(workspace.slug) - .resources() - .providers() - .integrations() - .azure(); - - const nextStepsUrl = `${baseUrl}/${azureUrl}`; - - if (resourceProviderId != null) - return db - .update(SCHEMA.resourceProviderAzure) - .set({ tenantId: tenant.id, subscriptionId }) - .where( - eq( - SCHEMA.resourceProviderAzure.resourceProviderId, - resourceProviderId, - ), - ) - .then(() => - resourceScanQueue.add(resourceProviderId, { resourceProviderId }), - ) - .then(() => - NextResponse.redirect(nextStepsUrl + `/${resourceProviderId}`), - ) - .catch((error) => { - logger.error(error); - return NextResponse.json( - { error: "Failed to update resource provider" }, - { status: INTERNAL_SERVER_ERROR }, - ); - }); - - return createResourceProvider( - db, - workspaceId, - tenant.id, - subscriptionId, - name, - ) - .then((rp) => NextResponse.redirect(nextStepsUrl + `/${rp.id}`)) - .catch((error) => { - logger.error(error); - return NextResponse.json( - { error: "Failed to create resource provider" }, - { status: INTERNAL_SERVER_ERROR }, - ); - }); - }); -}; diff --git a/apps/webservice/src/app/api/azure/consent/route.ts b/apps/webservice/src/app/api/azure/consent/route.ts deleted file mode 100644 index c94cd5487..000000000 --- a/apps/webservice/src/app/api/azure/consent/route.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { BAD_REQUEST, INTERNAL_SERVER_ERROR, NOT_FOUND } from "http-status"; -import ms from "ms"; -import { z } from "zod"; - -import { redis } from "@ctrlplane/api"; -import { eq, takeFirstOrNull } from "@ctrlplane/db"; -import { db } from "@ctrlplane/db/client"; -import * as SCHEMA from "@ctrlplane/db/schema"; -import { Channel, getQueue } from "@ctrlplane/events"; - -import { env } from "~/env"; - -const configSchema = z.object({ - workspaceId: z.string(), - tenantId: z.string(), - subscriptionId: z.string(), - name: z.string(), -}); - -const resourceScanQueue = getQueue(Channel.ResourceScan); - -export const GET = async (req: NextRequest) => { - const { searchParams } = new URL(req.url); - const state = searchParams.get("state"); - const resourceProviderId = searchParams.get("resourceProviderId"); - if (!state) - return NextResponse.json({ error: "Bad request" }, { status: BAD_REQUEST }); - - const redisKey = `azure_consent_state:${state}`; - const configJSON = await redis.get(redisKey); - if (configJSON == null) - return NextResponse.json({ error: "Bad request" }, { status: BAD_REQUEST }); - await redis.del(redisKey); - - const config = JSON.parse(configJSON); - const parsedConfig = configSchema.safeParse(config); - if (!parsedConfig.success) - return NextResponse.json({ error: "Bad request" }, { status: BAD_REQUEST }); - - const { workspaceId, tenantId, subscriptionId, name } = parsedConfig.data; - - return db.transaction(async (db) => { - const workspace = await db - .select() - .from(SCHEMA.workspace) - .where(eq(SCHEMA.workspace.id, workspaceId)) - .then(takeFirstOrNull); - - if (workspace == null) - return NextResponse.json( - { error: "Workspace not found" }, - { status: NOT_FOUND }, - ); - - const tenant = await db - .insert(SCHEMA.azureTenant) - .values({ workspaceId, tenantId }) - .returning() - .then(takeFirstOrNull); - - if (tenant == null) - return NextResponse.json( - { error: "Failed to create tenant" }, - { status: INTERNAL_SERVER_ERROR }, - ); - - const nextStepsUrl = `${env.BASE_URL}/${workspace.slug}/resource-providers/integrations/azure/${resourceProviderId}`; - - if (resourceProviderId != null) - return db - .update(SCHEMA.resourceProviderAzure) - .set({ tenantId, subscriptionId }) - .where( - eq( - SCHEMA.resourceProviderAzure.resourceProviderId, - resourceProviderId, - ), - ) - .then(() => NextResponse.redirect(nextStepsUrl)) - .catch(() => - NextResponse.json( - { error: "Failed to update resource provider" }, - { status: INTERNAL_SERVER_ERROR }, - ), - ); - - const resourceProvider = await db - .insert(SCHEMA.resourceProvider) - .values({ workspaceId, name }) - .returning() - .then(takeFirstOrNull); - - if (resourceProvider == null) - return NextResponse.json( - { error: "Failed to create resource provider" }, - { status: INTERNAL_SERVER_ERROR }, - ); - - return db - .insert(SCHEMA.resourceProviderAzure) - .values({ - resourceProviderId: resourceProvider.id, - tenantId: tenant.id, - subscriptionId, - }) - .then(() => - resourceScanQueue.add( - resourceProvider.id, - { resourceProviderId: resourceProvider.id }, - { repeat: { every: ms("10m"), immediately: true } }, - ), - ) - .then(() => - NextResponse.redirect( - `${env.BASE_URL}/${workspace.slug}/resource-providers/integrations/azure/${resourceProvider.id}`, - ), - ) - .catch(() => - NextResponse.json( - { error: "Failed to create resource provider" }, - { status: INTERNAL_SERVER_ERROR }, - ), - ); - }); -}; diff --git a/apps/webservice/src/app/api/v1/release-targets/[releaseTargetId]/evaluate/route.ts b/apps/webservice/src/app/api/v1/release-targets/[releaseTargetId]/evaluate/route.ts deleted file mode 100644 index 2f2a38738..000000000 --- a/apps/webservice/src/app/api/v1/release-targets/[releaseTargetId]/evaluate/route.ts +++ /dev/null @@ -1,240 +0,0 @@ -import type { Tx } from "@ctrlplane/db"; -import { NextResponse } from "next/server"; -import { INTERNAL_SERVER_ERROR, NOT_FOUND } from "http-status"; - -import { and, desc, eq, sql, takeFirst } from "@ctrlplane/db"; -import { createReleaseJob } from "@ctrlplane/db/queries"; -import * as schema from "@ctrlplane/db/schema"; -import { Channel, dispatchQueueJob, getQueue } from "@ctrlplane/events"; -import { logger } from "@ctrlplane/logger"; -import { - VariableReleaseManager, - VersionReleaseManager, -} from "@ctrlplane/rule-engine"; -import { Permission } from "@ctrlplane/validators/auth"; - -import { authn, authz } from "~/app/api/v1/auth"; -import { request } from "~/app/api/v1/middleware"; - -const log = logger.child({ - component: "release-target-evaluate-route", -}); - -const logErr = (releaseTargetId: string, e: any) => { - const estr = JSON.stringify(e, null, 2); - console.error( - `Failed to evaluate release target ${releaseTargetId}: ${estr}`, - ); - log.error(`Failed to evaluate release target ${releaseTargetId}: ${estr}`); -}; - -const handleVersionRelease = async (tx: Tx, releaseTarget: any) => { - try { - log.info("Evaluating version release", { - releaseTargetId: releaseTarget.id, - }); - const workspaceId = releaseTarget.resource.workspaceId; - - const vrm = new VersionReleaseManager(tx, { - ...releaseTarget, - workspaceId, - }); - - const { chosenCandidate } = await vrm.evaluate(); - - log.info( - `Version release evaluated: ${JSON.stringify(chosenCandidate, null, 2)}`, - ); - - if (!chosenCandidate) return null; - - const { release: versionRelease } = await vrm.upsertRelease( - chosenCandidate.id, - ); - - log.info( - `Version release upserted: ${JSON.stringify(versionRelease, null, 2)}`, - ); - - return versionRelease; - } catch (e: any) { - logErr(releaseTarget.id, e); - return null; - } -}; - -const handleVariableRelease = async (tx: Tx, releaseTarget: any) => { - try { - log.info("Evaluating variable release", { - releaseTargetId: releaseTarget.id, - }); - - const workspaceId = releaseTarget.resource.workspaceId; - - const varrm = new VariableReleaseManager(tx, { - ...releaseTarget, - workspaceId, - }); - - const { chosenCandidate: variableValues } = await varrm.evaluate(); - - log.info( - `Variable release evaluated: ${JSON.stringify(variableValues, null, 2)}`, - ); - - const { release: variableRelease } = - await varrm.upsertRelease(variableValues); - - log.info( - `Variable release upserted: ${JSON.stringify(variableRelease, null, 2)}`, - ); - - return variableRelease; - } catch (e: any) { - logErr(releaseTarget.id, e); - return null; - } -}; - -export const POST = request() - .use(authn) - .use( - authz(({ can, params }) => - can.perform(Permission.ReleaseTargetGet).on({ - type: "releaseTarget", - id: params.releaseTargetId ?? "", - }), - ), - ) - .handle }>( - async ({ db }, { params }) => { - const { releaseTargetId } = await params; - - const rt = await db.query.releaseTarget.findFirst({ - where: eq(schema.releaseTarget.id, releaseTargetId), - with: { - resource: true, - environment: true, - deployment: true, - }, - }); - - if (rt == null) { - log.error("Release target not found", { releaseTargetId }); - return NextResponse.json( - { error: "Release target not found" }, - { status: NOT_FOUND }, - ); - } - - try { - const release = await db.transaction(async (tx) => { - await tx.execute( - sql` - SELECT * FROM ${schema.releaseTarget} - INNER JOIN ${schema.computedPolicyTargetReleaseTarget} ON ${eq(schema.computedPolicyTargetReleaseTarget.releaseTargetId, schema.releaseTarget.id)} - INNER JOIN ${schema.policyTarget} ON ${eq(schema.computedPolicyTargetReleaseTarget.policyTargetId, schema.policyTarget.id)} - WHERE ${eq(schema.releaseTarget.id, rt.id)} - FOR UPDATE NOWAIT - `, - ); - - const existingVersionRelease = - await tx.query.versionRelease.findFirst({ - where: eq(schema.versionRelease.releaseTargetId, rt.id), - orderBy: desc(schema.versionRelease.createdAt), - }); - - const existingVariableRelease = - await tx.query.variableSetRelease.findFirst({ - where: eq(schema.variableSetRelease.releaseTargetId, rt.id), - orderBy: desc(schema.variableSetRelease.createdAt), - }); - - const [versionRelease, variableRelease] = await Promise.all([ - handleVersionRelease(tx, rt), - handleVariableRelease(tx, rt), - ]); - - if (versionRelease == null || variableRelease == null) return null; - - const hasSameVersion = - existingVersionRelease?.id === versionRelease.id; - const hasSameVariables = - existingVariableRelease?.id === variableRelease.id; - - if (hasSameVersion && hasSameVariables) { - return tx.query.release.findFirst({ - where: and( - eq(schema.release.versionReleaseId, versionRelease.id), - eq(schema.release.variableReleaseId, variableRelease.id), - ), - }); - } - - return tx - .insert(schema.release) - .values({ - versionReleaseId: versionRelease.id, - variableReleaseId: variableRelease.id, - }) - .returning() - .then(takeFirst); - }); - - if (release == null) { - log.error( - "Failed to evaluate release target because release was null", - { - releaseTargetId, - release, - }, - ); - return NextResponse.json( - { error: "Failed to evaluate release target" }, - { status: INTERNAL_SERVER_ERROR }, - ); - } - - // Check if a job already exists for this release - const existingReleaseJob = await db.query.releaseJob.findFirst({ - where: eq(schema.releaseJob.releaseId, release.id), - }); - - if (existingReleaseJob != null) { - log.info("Release job already exists for release", { - releaseTargetId, - releaseId: release.id, - }); - return NextResponse.json(release); - } - - const newReleaseJob = await db.transaction(async (tx) => - createReleaseJob(tx, release), - ); - - getQueue(Channel.DispatchJob).add(newReleaseJob.id, { - jobId: newReleaseJob.id, - }); - - return NextResponse.json(release); - } catch (e: any) { - const estr = JSON.stringify(e, null, 2); - const isRowLocked = e.code === "55P03"; - const isReleaseTargetNotCommittedYet = e.code === "23503"; - if (isRowLocked || isReleaseTargetNotCommittedYet) { - dispatchQueueJob().toEvaluate().releaseTargets([rt]); - return NextResponse.json({ - message: "Release target is being evaluated", - }); - } - - logErr(releaseTargetId, e); - - return NextResponse.json( - { error: `Failed to evaluate release target: ${estr}` }, - { status: INTERNAL_SERVER_ERROR }, - ); - } - }, - ); diff --git a/apps/webservice/src/env.ts b/apps/webservice/src/env.ts index 4bce9934f..d812c3063 100644 --- a/apps/webservice/src/env.ts +++ b/apps/webservice/src/env.ts @@ -30,7 +30,6 @@ export const env = createEnv({ OPENREPLAY_PROJECT_KEY: z.string().optional(), OPENREPLAY_INGEST_POINT: z.string().optional(), AZURE_APP_CLIENT_ID: z.string().optional(), - REDIS_URL: z.string(), }, /** diff --git a/apps/webservice/src/instrumentation-node.ts b/apps/webservice/src/instrumentation-node.ts index a8aa4a7b0..ca9e4e7ea 100644 --- a/apps/webservice/src/instrumentation-node.ts +++ b/apps/webservice/src/instrumentation-node.ts @@ -35,9 +35,6 @@ const sdk = new NodeSDK({ enhancedDatabaseReporting: true, addSqlCommenterCommentToQueries: true, }, - "@opentelemetry/instrumentation-ioredis": { - enabled: true, - }, "@opentelemetry/instrumentation-winston": { enabled: true, }, diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index 9b96ebb27..127be89c1 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -12,25 +12,6 @@ services: volumes: - db-data:/var/lib/postgresql/data - redis: - image: redis:latest - container_name: ctrlplane-redis - restart: always - ports: - - "6379:6379" - volumes: - - redis-data:/data - - redisinsight: - image: redis/redisinsight:latest - container_name: ctrlplane-redisinsight - links: - - redis - ports: - - "5540:5540" - volumes: - - redisinsight-data:/data - # otel-collector: # image: otel/opentelemetry-collector-contrib:latest # container_name: ctrlplane-otel-collector @@ -71,5 +52,3 @@ services: volumes: db-data: - redis-data: - redisinsight-data: diff --git a/docker-compose.yaml b/docker-compose.yaml index 089595568..68faefb3b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -17,16 +17,6 @@ services: timeout: 5s retries: 5 - redis: - platform: linux/amd64 - image: redis:7 - container_name: ctrlplane-redis - restart: always - ports: - - "6379:6379" - volumes: - - redis_data:/data - migrations: build: context: . @@ -61,7 +51,6 @@ services: NEXTAUTH_URL: ${BASE_URL} OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4318" - REDIS_URL: redis://redis:6379 POSTGRES_URL: postgresql://ctrlplane:ctrlplane@postgres:5432/ctrlplane VARIABLES_AES_256_KEY: d2506d733ee210078461c08ee8e1605be75ed73b0941d4b513ab7b159c6fbcd9 depends_on: @@ -78,7 +67,6 @@ services: container_name: ctrlplane-jobs environment: POSTGRES_URL: "postgres://ctrlplane:ctrlplane@postgres:5432/ctrlplane" - REDIS_URL: "redis://redis:6379" VARIABLES_AES_256_KEY: d2506d733ee210078461c08ee8e1605be75ed73b0941d4b513ab7b159c6fbcd9 depends_on: - postgres @@ -91,11 +79,9 @@ services: container_name: ctrlplane-event-worker environment: POSTGRES_URL: "postgres://ctrlplane:ctrlplane@postgres:5432/ctrlplane" - REDIS_URL: "redis://redis:6379" VARIABLES_AES_256_KEY: d2506d733ee210078461c08ee8e1605be75ed73b0941d4b513ab7b159c6fbcd9 depends_on: - postgres - - redis otel-collector: image: otel/opentelemetry-collector-contrib:latest @@ -112,4 +98,3 @@ services: volumes: postgres_data: - redis_data: diff --git a/packages/api/package.json b/packages/api/package.json index e1af5d40b..8d89a8194 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "@aws-sdk/client-iam": "^3.696.0", + "@aws-sdk/client-sts": "^3.696.0", "@ctrlplane/auth": "workspace:*", "@ctrlplane/db": "workspace:*", "@ctrlplane/events": "workspace:*", diff --git a/packages/api/src/config.ts b/packages/api/src/config.ts index 28d9d3fa7..cf9a64787 100644 --- a/packages/api/src/config.ts +++ b/packages/api/src/config.ts @@ -4,7 +4,6 @@ import { z } from "zod"; export const env = createEnv({ server: { BASE_URL: z.string().url().default("http://localhost:3000"), - REDIS_URL: z.string(), GITHUB_URL: z.string().url().optional(), GITHUB_BOT_NAME: z.string().min(1).optional(), diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 90fe97093..1cbe6fdd7 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -31,4 +31,3 @@ type RouterOutputs = inferRouterOutputs; export { createTRPCContext, appRouter, createCaller }; export type { AppRouter, RouterInputs, RouterOutputs }; -export * from "./redis"; diff --git a/packages/api/src/redis.ts b/packages/api/src/redis.ts deleted file mode 100644 index 96845f67f..000000000 --- a/packages/api/src/redis.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IORedis from "ioredis"; - -import { env } from "./config"; - -export const redis = new IORedis(env.REDIS_URL, { maxRetriesPerRequest: null }); diff --git a/packages/api/src/router/policy/approval/add-record.ts b/packages/api/src/router/policy/approval/add-record.ts index f0e704be5..6fe225813 100644 --- a/packages/api/src/router/policy/approval/add-record.ts +++ b/packages/api/src/router/policy/approval/add-record.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { and, eq, inArray } from "@ctrlplane/db"; import * as SCHEMA from "@ctrlplane/db/schema"; -import { dispatchQueueJob } from "@ctrlplane/events"; +import { eventDispatcher } from "@ctrlplane/events"; import { Permission } from "@ctrlplane/validators/auth"; import { protectedProcedure } from "../../../trpc"; @@ -61,9 +61,10 @@ export const addRecord = protectedProcedure ) .then((rows) => rows.map((row) => row.release_target)); - await dispatchQueueJob() - .toEvaluate() - .releaseTargets(affectedReleaseTargets); + for (const releaseTarget of affectedReleaseTargets) + await eventDispatcher.dispatchEvaluateReleaseTarget(releaseTarget, { + skipDuplicateCheck: true, + }); return record; }); diff --git a/packages/api/src/router/policy/version-selector.ts b/packages/api/src/router/policy/version-selector.ts index a9c82a568..725f25c2c 100644 --- a/packages/api/src/router/policy/version-selector.ts +++ b/packages/api/src/router/policy/version-selector.ts @@ -1,26 +1,20 @@ -import type { Tx } from "@ctrlplane/db"; import { z } from "zod"; import { buildConflictUpdateColumns, eq, + rulesAndTargets, takeFirst, takeFirstOrNull, } from "@ctrlplane/db"; import * as schema from "@ctrlplane/db/schema"; -import { dispatchQueueJob } from "@ctrlplane/events"; +import { eventDispatcher } from "@ctrlplane/events"; import { getApplicablePolicies } from "@ctrlplane/rule-engine/db"; import { Permission } from "@ctrlplane/validators/auth"; import { deploymentVersionCondition } from "@ctrlplane/validators/releases"; import { createTRPCRouter, protectedProcedure } from "../../trpc"; -const getPolicyTargets = async (db: Tx, policyId: string) => - db - .select() - .from(schema.policyTarget) - .where(eq(schema.policyTarget.policyId, policyId)); - export const policyVersionSelectorRouter = createTRPCRouter({ byPolicyId: protectedProcedure .input(z.string().uuid()) @@ -98,12 +92,13 @@ export const policyVersionSelectorRouter = createTRPCRouter({ .returning() .then(takeFirst); - const policyTargets = await getPolicyTargets(ctx.db, policyId); - for (const policyTarget of policyTargets) - dispatchQueueJob() - .toCompute() - .policyTarget(policyTarget) - .releaseTargetSelector(); + const fullPolicy = await ctx.db.query.policy.findFirst({ + where: eq(schema.policy.id, policyId), + with: rulesAndTargets, + }); + + if (fullPolicy == null) throw new Error("Policy not found"); + await eventDispatcher.dispatchPolicyUpdated(fullPolicy, fullPolicy); return vs; }), @@ -136,12 +131,12 @@ export const policyVersionSelectorRouter = createTRPCRouter({ .returning() .then(takeFirst); - const policyTargets = await getPolicyTargets(ctx.db, policyId); - for (const policyTarget of policyTargets) - dispatchQueueJob() - .toCompute() - .policyTarget(policyTarget) - .releaseTargetSelector(); + const fullPolicy = await ctx.db.query.policy.findFirst({ + where: eq(schema.policy.id, policyId), + with: rulesAndTargets, + }); + if (fullPolicy == null) throw new Error("Policy not found"); + await eventDispatcher.dispatchPolicyUpdated(fullPolicy, fullPolicy); return vs; }), diff --git a/packages/api/src/router/redeploy.ts b/packages/api/src/router/redeploy.ts index 951084efe..ac721a73d 100644 --- a/packages/api/src/router/redeploy.ts +++ b/packages/api/src/router/redeploy.ts @@ -6,7 +6,7 @@ import { z } from "zod"; import { and, eq, takeFirstOrNull } from "@ctrlplane/db"; import { createReleaseJob } from "@ctrlplane/db/queries"; import * as schema from "@ctrlplane/db/schema"; -import { Channel, eventDispatcher, getQueue } from "@ctrlplane/events"; +import { eventDispatcher } from "@ctrlplane/events"; import { Permission } from "@ctrlplane/validators/auth"; import { createTRPCRouter, protectedProcedure } from "../trpc"; @@ -38,8 +38,7 @@ const createForceDeployment = async ( message: "No release exists for this target", }); - const job = await createReleaseJob(tx, existingRelease.release); - getQueue(Channel.DispatchJob).add(job.id, { jobId: job.id }); + await createReleaseJob(tx, existingRelease.release); return existingRelease; }); diff --git a/packages/api/src/router/resource/resource-provider.ts b/packages/api/src/router/resource/resource-provider.ts index 4af82ca94..c241369d1 100644 --- a/packages/api/src/router/resource/resource-provider.ts +++ b/packages/api/src/router/resource/resource-provider.ts @@ -1,4 +1,3 @@ -import ms from "ms"; import { z } from "zod"; import { @@ -24,14 +23,11 @@ import { updateResourceProviderAws, updateResourceProviderGoogle, } from "@ctrlplane/db/schema"; -import { Channel, getQueue } from "@ctrlplane/events"; import { Permission } from "@ctrlplane/validators/auth"; import { createTRPCRouter, protectedProcedure } from "../../trpc"; import { resourceProviderPageRouter } from "../resource-provider-page/router"; -const resourceScanQueue = getQueue(Channel.ResourceScan); - export const resourceProviderRouter = createTRPCRouter({ page: resourceProviderPageRouter, @@ -176,9 +172,7 @@ export const resourceProviderRouter = createTRPCRouter({ .on({ type: "resourceProvider", id: input }), }) .input(z.string().uuid()) - .mutation(({ input }) => - resourceScanQueue.add(input, { resourceProviderId: input }), - ), + .mutation(() => void 0), google: createTRPCRouter({ create: protectedProcedure @@ -213,12 +207,6 @@ export const resourceProviderRouter = createTRPCRouter({ .returning() .then(takeFirst); - await resourceScanQueue.add( - tg.id, - { resourceProviderId: tg.id }, - { repeat: { every: ms("10m"), immediately: true } }, - ); - return { ...tg, config: tgConfig }; }), ), @@ -257,23 +245,8 @@ export const resourceProviderRouter = createTRPCRouter({ .then(takeFirst); if (input.repeatSeconds != null) { - await resourceScanQueue.remove(input.resourceProviderId); - await resourceScanQueue.add( - input.resourceProviderId, - { resourceProviderId: input.resourceProviderId }, - { - repeat: { - every: input.repeatSeconds * 1000, - immediately: true, - }, - }, - ); return; } - - await resourceScanQueue.add(input.resourceProviderId, { - resourceProviderId: input.resourceProviderId, - }); }); }), }), @@ -311,12 +284,6 @@ export const resourceProviderRouter = createTRPCRouter({ .returning() .then(takeFirst); - await resourceScanQueue.add( - provider.id, - { resourceProviderId: provider.id }, - { repeat: { every: ms("10m"), immediately: true } }, - ); - return { ...provider, config: providerConfig }; }), ), @@ -361,22 +328,8 @@ export const resourceProviderRouter = createTRPCRouter({ .then(takeFirst); if (input.repeatSeconds != null) { - await resourceScanQueue.remove(input.resourceProviderId); - await resourceScanQueue.add( - input.resourceProviderId, - { resourceProviderId: input.resourceProviderId }, - { - repeat: { - every: input.repeatSeconds * 1000, - immediately: true, - }, - }, - ); return; } - await resourceScanQueue.add(input.resourceProviderId, { - resourceProviderId: input.resourceProviderId, - }); }); }), }), @@ -529,10 +482,6 @@ export const resourceProviderRouter = createTRPCRouter({ .returning() .then(takeFirst); - // We should think about the edge case here, if a scan is in progress, - // what do we do? - await resourceScanQueue.remove(input.providerId); - return deletedProvider; }), ), diff --git a/packages/db/package.json b/packages/db/package.json index 91f53a138..662ed9128 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -46,11 +46,9 @@ "@t3-oss/env-core": "catalog:", "drizzle-orm": "^0.37.0", "drizzle-zod": "^0.5.1", - "ioredis": "catalog:", "lodash": "catalog:", "ms": "catalog:", "pg": "^8.14.1", - "redis-semaphore": "catalog:", "rrule": "^2.8.1", "ts-is-present": "catalog:", "zod": "catalog:" diff --git a/packages/db/src/config.ts b/packages/db/src/config.ts index 0ff692f43..29eda5b0f 100644 --- a/packages/db/src/config.ts +++ b/packages/db/src/config.ts @@ -14,8 +14,6 @@ export const env = createEnv({ .transform((value) => parseInt(value)), POSTGRES_APPLICATION_NAME: z.string().default("ctrlplane"), - - REDIS_URL: z.string().url().default("redis://127.0.0.1:6379"), }, runtimeEnv: process.env, emptyStringAsUndefined: true, diff --git a/packages/db/src/redis.ts b/packages/db/src/redis.ts deleted file mode 100644 index 38e712e90..000000000 --- a/packages/db/src/redis.ts +++ /dev/null @@ -1,13 +0,0 @@ -import IORedis from "ioredis"; - -import { logger } from "@ctrlplane/logger"; - -import { env } from "./config.js"; - -const log = logger.child({ module: "redis" }); - -const config = { maxRetriesPerRequest: null }; -export const redis = new IORedis(env.REDIS_URL, config); - -redis.on("connect", () => log.info("Redis connected")); -redis.on("error", (err) => log.error("Redis error", { err })); diff --git a/packages/events/package.json b/packages/events/package.json index 0187b07b0..79127a006 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -25,10 +25,7 @@ "@ctrlplane/validators": "workspace:*", "@ctrlplane/workspace-engine-sdk": "workspace:*", "@t3-oss/env-core": "catalog:", - "bullmq": "catalog:", - "bullmq-otel": "^1.0.1", "date-fns": "^4.1.0", - "ioredis": "catalog:", "kafkajs": "^2.2.4", "lodash": "catalog:", "ts-is-present": "catalog:", diff --git a/packages/events/src/config.ts b/packages/events/src/config.ts index 5bac78285..5025d9da9 100644 --- a/packages/events/src/config.ts +++ b/packages/events/src/config.ts @@ -3,12 +3,7 @@ import { z } from "zod"; export const env = createEnv({ server: { - REDIS_URL: z.string(), KAFKA_BROKERS: z.string().default("localhost:9092"), - KAFKA_EVENT_QUEUE_ENABLED: z - .enum(["true", "false"]) - .default("false") - .transform((value) => value === "true"), }, runtimeEnv: process.env, emptyStringAsUndefined: true, diff --git a/packages/events/src/dispatch-jobs.ts b/packages/events/src/dispatch-jobs.ts deleted file mode 100644 index 8e08e6c91..000000000 --- a/packages/events/src/dispatch-jobs.ts +++ /dev/null @@ -1,361 +0,0 @@ -import type * as schema from "@ctrlplane/db/schema"; -import type { - ReleaseTargetIdentifier, - VersionEvaluateOptions, -} from "@ctrlplane/rule-engine"; -import _ from "lodash"; - -import type { EventDispatcher, FullPolicy } from "./event-dispatcher.js"; -import { Channel, getQueue } from "./index.js"; - -const dispatchUpdatedResourceJob = async (resources: schema.Resource[]) => { - const q = getQueue(Channel.UpdatedResource); - const waiting = await q.getWaiting(); - const waitingIds = new Set(waiting.map((job) => job.data.id)); - const resourcesNotAlreadyQueued = resources.filter( - (resource) => !waitingIds.has(resource.id), - ); - - const insertJobs = resourcesNotAlreadyQueued.map((r) => ({ - name: r.id, - data: r, - })); - await q.addBulk(insertJobs); -}; - -const dispatchEvaluateJobs = async ( - rts: ReleaseTargetIdentifier[], - opts?: { - skipDuplicateCheck?: boolean; - versionEvaluateOptions?: VersionEvaluateOptions; - }, -) => { - const q = getQueue(Channel.EvaluateReleaseTarget); - const waiting = await q.getWaiting(); - const rtsToEvaluate = rts.filter( - (rt) => - !waiting.some( - (job) => - job.data.deploymentId === rt.deploymentId && - job.data.environmentId === rt.environmentId && - job.data.resourceId === rt.resourceId, - ), - ); - - for (const rt of rtsToEvaluate) - await q.add(`${rt.resourceId}-${rt.environmentId}-${rt.deploymentId}`, { - ...rt, - ...opts, - }); -}; - -const dispatchComputeDeploymentResourceSelectorJobs = async ( - deployment: schema.Deployment, -) => { - const { id } = deployment; - const q = getQueue(Channel.ComputeDeploymentResourceSelector); - const waiting = await q.getWaiting(); - const isAlreadyQueued = waiting.some((job) => job.data.id === id); - if (isAlreadyQueued) return; - await q.add(id, deployment); -}; - -const dispatchComputeEnvironmentResourceSelectorJobs = async ( - environment: schema.Environment, -) => { - const { id } = environment; - const q = getQueue(Channel.ComputeEnvironmentResourceSelector); - const waiting = await q.getWaiting(); - const isAlreadyQueued = waiting.some((job) => job.data.id === id); - if (isAlreadyQueued) return; - await q.add(id, environment); -}; - -const dispatchComputePolicyTargetReleaseTargetSelectorJobs = async ( - policyTarget: schema.PolicyTarget, -) => { - const { id } = policyTarget; - const q = getQueue(Channel.ComputePolicyTargetReleaseTargetSelector); - const waiting = await q.getWaiting(); - const isAlreadyQueued = waiting.some((job) => job.data.id === id); - if (isAlreadyQueued) return; - await q.add(id, policyTarget); -}; - -const dispatchComputeSystemReleaseTargetsJobs = async ( - system: schema.System, -) => { - const { id } = system; - const q = getQueue(Channel.ComputeSystemsReleaseTargets); - const waiting = await q.getWaiting(); - const isAlreadyQueued = waiting.some((job) => job.data.id === id); - if (isAlreadyQueued) return; - await q.add(id, { id }); -}; - -const dispatchComputeWorkspacePolicyTargetsJobs = async ( - workspaceId: string, - releaseTargetsToEvaluate?: schema.ReleaseTarget[], -) => { - const q = getQueue(Channel.ComputeWorkspacePolicyTargets); - const waiting = await q.getWaiting(); - const isAlreadyQueued = waiting.some( - (job) => - job.data.workspaceId === workspaceId && - _.isEqual(job.data.releaseTargetsToEvaluate, releaseTargetsToEvaluate), - ); - if (isAlreadyQueued) return; - await q.add(workspaceId, { - workspaceId, - releaseTargetsToEvaluate, - }); -}; - -const toEvaluate = () => ({ - releaseTargets: ( - releaseTargets: ReleaseTargetIdentifier[], - opts?: { - skipDuplicateCheck?: boolean; - versionEvaluateOptions?: VersionEvaluateOptions; - }, - ) => dispatchEvaluateJobs(releaseTargets, opts), -}); - -const toCompute = () => ({ - deployment: (deployment: schema.Deployment) => ({ - resourceSelector: () => - dispatchComputeDeploymentResourceSelectorJobs(deployment), - }), - environment: (environment: schema.Environment) => ({ - resourceSelector: () => - dispatchComputeEnvironmentResourceSelectorJobs(environment), - }), - policyTarget: (policyTarget: schema.PolicyTarget) => ({ - releaseTargetSelector: () => - dispatchComputePolicyTargetReleaseTargetSelectorJobs(policyTarget), - }), - system: (system: schema.System) => ({ - releaseTargets: () => dispatchComputeSystemReleaseTargetsJobs(system), - }), - workspace: (workspaceId: string) => ({ - policyTargets: (opts?: { - releaseTargetsToEvaluate?: schema.ReleaseTarget[]; - }) => - dispatchComputeWorkspacePolicyTargetsJobs( - workspaceId, - opts?.releaseTargetsToEvaluate, - ), - }), -}); - -const toDispatch = () => ({ - ctrlplaneJob: (jobId: string) => - getQueue(Channel.DispatchJob).add(jobId, { - jobId, - }), -}); - -export const dispatchQueueJob = () => ({ - toUpdatedResource: dispatchUpdatedResourceJob, - toEvaluate, - toCompute, - toDispatch, -}); - -export class BullMQEventDispatcher implements EventDispatcher { - async dispatchResourceCreated(resource: schema.Resource): Promise { - await getQueue(Channel.NewResource).add(resource.id, resource); - } - - async dispatchResourceUpdated( - _: schema.Resource, - current: schema.Resource, - ): Promise { - const q = getQueue(Channel.UpdatedResource); - const waiting = await q.getWaiting(); - const waitingIds = new Set(waiting.map((job) => job.data.id)); - const isAlreadyQueued = waitingIds.has(current.id); - if (isAlreadyQueued) return; - await q.add(current.id, current); - } - - async dispatchResourceDeleted(resource: schema.Resource): Promise { - await getQueue(Channel.DeleteResource).add(resource.id, resource); - } - - async dispatchResourceVariableCreated( - resourceVariable: typeof schema.resourceVariable.$inferSelect, - ): Promise { - await getQueue(Channel.UpdateResourceVariable).add( - resourceVariable.id, - resourceVariable, - ); - } - - async dispatchResourceVariableUpdated( - _: typeof schema.resourceVariable.$inferSelect, - current: typeof schema.resourceVariable.$inferSelect, - ): Promise { - await getQueue(Channel.UpdateResourceVariable).add(current.id, current); - } - - async dispatchResourceVariableDeleted( - _: typeof schema.resourceVariable.$inferSelect, - ): Promise { - await Promise.resolve(); - } - - async dispatchEnvironmentCreated( - environment: schema.Environment, - ): Promise { - await getQueue(Channel.NewEnvironment).add(environment.id, environment); - } - - async dispatchEnvironmentUpdated( - previous: schema.Environment, - current: schema.Environment, - ): Promise { - await getQueue(Channel.UpdateEnvironment).add(current.id, { - ...current, - oldSelector: previous.resourceSelector, - }); - } - - async dispatchEnvironmentDeleted( - environment: schema.Environment, - ): Promise { - await getQueue(Channel.DeleteEnvironment).add(environment.id, environment); - } - - async dispatchDeploymentCreated( - deployment: schema.Deployment, - ): Promise { - await getQueue(Channel.NewDeployment).add(deployment.id, deployment); - } - - async dispatchDeploymentUpdated( - previous: schema.Deployment, - current: schema.Deployment, - ): Promise { - await getQueue(Channel.UpdateDeployment).add(current.id, { - new: current, - old: previous, - }); - } - - async dispatchDeploymentDeleted( - deployment: schema.Deployment, - ): Promise { - await getQueue(Channel.DeleteDeployment).add(deployment.id, deployment); - } - - async dispatchDeploymentVersionCreated( - deploymentVersion: schema.DeploymentVersion, - ): Promise { - await getQueue(Channel.NewDeploymentVersion).add( - deploymentVersion.id, - deploymentVersion, - ); - } - - async dispatchDeploymentVersionUpdated( - _: schema.DeploymentVersion, - current: schema.DeploymentVersion, - ): Promise { - await getQueue(Channel.NewDeploymentVersion).add(current.id, current); - } - - async dispatchDeploymentVersionDeleted( - _: schema.DeploymentVersion, - ): Promise { - await Promise.resolve(); - } - - async dispatchDeploymentVariableCreated( - deploymentVariable: schema.DeploymentVariable, - ): Promise { - await getQueue(Channel.UpdateDeploymentVariable).add( - deploymentVariable.id, - deploymentVariable, - ); - } - - async dispatchDeploymentVariableUpdated( - _: schema.DeploymentVariable, - current: schema.DeploymentVariable, - ): Promise { - await getQueue(Channel.UpdateDeploymentVariable).add(current.id, current); - } - - async dispatchDeploymentVariableValueCreated( - _: schema.DeploymentVariableValue, - ): Promise { - await Promise.resolve(); - } - - async dispatchDeploymentVariableValueUpdated( - _: schema.DeploymentVariableValue, - __: schema.DeploymentVariableValue, - ): Promise { - await Promise.resolve(); - } - - async dispatchDeploymentVariableValueDeleted( - _: schema.DeploymentVariableValue, - ): Promise { - await Promise.resolve(); - } - - async dispatchDeploymentVariableDeleted( - _: schema.DeploymentVariable, - ): Promise { - await Promise.resolve(); - } - - async dispatchPolicyCreated(policy: FullPolicy): Promise { - await getQueue(Channel.NewPolicy).add(policy.id, policy); - } - - async dispatchPolicyUpdated( - _: FullPolicy, - current: FullPolicy, - ): Promise { - await getQueue(Channel.UpdatePolicy).add(current.id, current); - } - - async dispatchPolicyDeleted(_: FullPolicy): Promise { - await Promise.resolve(); - } - - async dispatchJobUpdated( - _: schema.Job & { metadata?: Record }, - current: schema.Job & { metadata?: Record }, - ): Promise { - await getQueue(Channel.UpdateJob).add(current.id, { - jobId: current.id, - data: current, - metadata: current.metadata, - }); - } - - async dispatchEvaluateReleaseTarget( - releaseTarget: schema.ReleaseTarget, - opts?: { skipDuplicateCheck?: boolean }, - ): Promise { - const q = getQueue(Channel.EvaluateReleaseTarget); - const waiting = await q.getWaiting(); - const isAlreadyQueued = waiting.some( - (job) => - job.data.environmentId === releaseTarget.environmentId && - job.data.resourceId === releaseTarget.resourceId && - job.data.deploymentId === releaseTarget.deploymentId, - ); - if (isAlreadyQueued) return; - await q.add(releaseTarget.id, { - environmentId: releaseTarget.environmentId, - resourceId: releaseTarget.resourceId, - deploymentId: releaseTarget.deploymentId, - skipDuplicateCheck: opts?.skipDuplicateCheck, - }); - } -} diff --git a/packages/events/src/index.ts b/packages/events/src/index.ts index 28a9391db..adcd5467d 100644 --- a/packages/events/src/index.ts +++ b/packages/events/src/index.ts @@ -1,48 +1,7 @@ -import type { Job, WorkerOptions } from "bullmq"; -import { Queue, Worker } from "bullmq"; -import { BullMQOtel } from "bullmq-otel"; - -import { logger } from "@ctrlplane/logger"; - import type { EventDispatcher } from "./event-dispatcher.js"; -import type { ChannelMap } from "./types.js"; -import { env } from "./config.js"; -import { BullMQEventDispatcher } from "./index.js"; import { KafkaEventDispatcher } from "./kafka/index.js"; -import { bullmqRedis } from "./redis.js"; - -export const createWorker = ( - name: T, - handler: (job: Job) => Promise, - opts?: Partial, -) => { - logger.info(`Creating worker ${name}`); - - return new Worker(String(name), handler, { - connection: bullmqRedis, - removeOnComplete: { age: 1 * 60 * 60, count: 5000 }, - removeOnFail: { age: 12 * 60 * 60, count: 5000 }, - concurrency: 5, - telemetry: new BullMQOtel("ctrlplane/event-worker"), - ...opts, - }); -}; - -const _queues = new Map(); -export const getQueue = (name: T) => { - if (!_queues.has(name)) { - _queues.set(name, new Queue(String(name), { connection: bullmqRedis })); - } - - return _queues.get(name) as Queue; -}; -export * from "./types.js"; -export * from "./redis.js"; export * from "./resource-provider-scan/handle-provider-scan.js"; -export * from "./dispatch-jobs.js"; export * from "./kafka/index.js"; -export const eventDispatcher: EventDispatcher = env.KAFKA_EVENT_QUEUE_ENABLED - ? new KafkaEventDispatcher() - : new BullMQEventDispatcher(); +export const eventDispatcher: EventDispatcher = new KafkaEventDispatcher(); diff --git a/packages/events/src/redis.ts b/packages/events/src/redis.ts deleted file mode 100644 index 2fa937dc8..000000000 --- a/packages/events/src/redis.ts +++ /dev/null @@ -1,7 +0,0 @@ -import IORedis from "ioredis"; - -import { env } from "./config.js"; - -export const bullmqRedis = new IORedis(env.REDIS_URL, { - maxRetriesPerRequest: null, -}); diff --git a/packages/events/src/types.ts b/packages/events/src/types.ts deleted file mode 100644 index 38a99be9c..000000000 --- a/packages/events/src/types.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type * as schema from "@ctrlplane/db/schema"; -import type { VersionEvaluateOptions } from "@ctrlplane/rule-engine"; -import type { ResourceCondition } from "@ctrlplane/validators/resources"; - -export enum Channel { - JobSync = "job-sync", - DispatchJob = "dispatch-job", - ResourceScan = "resource-scan", - - ReleaseNewVersion = "release-new-version", - ReleaseNewRepository = "release-new-repository", - ReleaseVariableChange = "release-variable-change", - - NewDeployment = "new-deployment", - NewDeploymentVersion = "new-deployment-version", - NewEnvironment = "new-environment", - NewRelease = "new-release", - NewResource = "new-resource", - NewPolicy = "new-policy", - - UpdatedResource = "updated-resource", - UpdateResourceVariable = "update-resource-variable", - UpdateEnvironment = "update-environment", - UpdateDeployment = "update-deployment", - UpdateDeploymentVariable = "update-deployment-variable", - UpdatePolicy = "update-policy", - UpdateJob = "update-job", - - DeleteDeployment = "delete-deployment", - DeleteEnvironment = "delete-environment", - DeleteResource = "delete-resource", - DeletedReleaseTarget = "deleted-release-target", // NOTE: handles post-processing for already deleted release targets - - EvaluateReleaseTarget = "evaluate-release-target", - - ComputeSystemsReleaseTargets = "compute-systems-release-targets", - ComputeEnvironmentResourceSelector = "compute-environment-resource-selector", - ComputeDeploymentResourceSelector = "compute-deployment-resource-selector", - ComputeWorkspacePolicyTargets = "compute-workspace-policy-targets", - ComputePolicyTargetReleaseTargetSelector = "compute-policy-target-release-target-selector", -} - -export type EvaluateReleaseTargetJob = { - environmentId: string; - resourceId: string; - deploymentId: string; - skipDuplicateCheck?: boolean; - versionEvaluateOptions?: VersionEvaluateOptions; -}; - -export type ChannelMap = { - [Channel.NewDeployment]: schema.Deployment; - [Channel.NewDeploymentVersion]: schema.DeploymentVersion; - [Channel.NewEnvironment]: typeof schema.environment.$inferSelect; - [Channel.NewResource]: schema.Resource; - [Channel.NewPolicy]: schema.Policy; - - [Channel.UpdateDeploymentVariable]: schema.DeploymentVariable; - [Channel.UpdateResourceVariable]: typeof schema.resourceVariable.$inferSelect; - [Channel.UpdateEnvironment]: schema.Environment & { - oldSelector: ResourceCondition | null; - }; - [Channel.UpdateDeployment]: { - new: schema.Deployment; - old: schema.Deployment; - }; - [Channel.UpdatedResource]: schema.Resource; - [Channel.UpdatePolicy]: schema.Policy; - [Channel.UpdateJob]: { - jobId: string; - data: schema.UpdateJob; - metadata?: Record; - }; - - [Channel.DeleteDeployment]: { id: string }; - [Channel.DeleteEnvironment]: { id: string }; - [Channel.DeleteResource]: schema.Resource; - [Channel.DeletedReleaseTarget]: schema.ReleaseTarget; - - [Channel.EvaluateReleaseTarget]: EvaluateReleaseTargetJob; - [Channel.DispatchJob]: { jobId: string }; - [Channel.ResourceScan]: { resourceProviderId: string }; - - [Channel.ComputeEnvironmentResourceSelector]: { id: string }; - [Channel.ComputeDeploymentResourceSelector]: { id: string }; - [Channel.ComputePolicyTargetReleaseTargetSelector]: { id: string }; - [Channel.ComputeWorkspacePolicyTargets]: { - workspaceId: string; - releaseTargetsToEvaluate?: schema.ReleaseTarget[]; - }; - [Channel.ComputeSystemsReleaseTargets]: { id: string }; -}; diff --git a/packages/job-dispatch/src/config.ts b/packages/job-dispatch/src/config.ts index fa215069b..264234193 100644 --- a/packages/job-dispatch/src/config.ts +++ b/packages/job-dispatch/src/config.ts @@ -1,11 +1,8 @@ /* eslint-disable no-restricted-properties */ import { createEnv } from "@t3-oss/env-core"; -import { z } from "zod"; export const env = createEnv({ - server: { - REDIS_URL: z.string(), - }, + server: {}, runtimeEnv: process.env, emptyStringAsUndefined: true, skipValidation: !!process.env.CI || !!process.env.SKIP_ENV_VALIDATION, diff --git a/packages/job-dispatch/src/job-dispatch.ts b/packages/job-dispatch/src/job-dispatch.ts index 64ef34a8f..529461e81 100644 --- a/packages/job-dispatch/src/job-dispatch.ts +++ b/packages/job-dispatch/src/job-dispatch.ts @@ -2,7 +2,6 @@ import type { Tx } from "@ctrlplane/db"; import { eq, takeFirst } from "@ctrlplane/db"; import * as schema from "@ctrlplane/db/schema"; -import { Channel, getQueue } from "@ctrlplane/events"; import { createTriggeredRunbookJob } from "./job-creation.js"; @@ -17,6 +16,5 @@ export const dispatchRunbook = async ( .where(eq(schema.runbook.id, runbookId)) .then(takeFirst); const job = await createTriggeredRunbookJob(db, runbook, values); - await getQueue(Channel.DispatchJob).add(job.id, { jobId: job.id }); return job; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42d8dc266..561620173 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,9 +33,6 @@ catalogs: '@types/ms': specifier: ^0.7.34 version: 0.7.34 - bullmq: - specifier: ^5.48.1 - version: 5.48.1 date-fns: specifier: ^3.6.0 version: 3.6.0 @@ -48,9 +45,6 @@ catalogs: eslint-plugin-vitest: specifier: ^0.5.4 version: 0.5.4 - ioredis: - specifier: ^5.4.1 - version: 5.4.1 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -69,9 +63,6 @@ catalogs: react-hook-form: specifier: ^7.54.2 version: 7.54.2 - redis-semaphore: - specifier: ^5.6.2 - version: 5.6.2 semver: specifier: ^7.7.1 version: 7.7.1 @@ -249,169 +240,6 @@ importers: specifier: 'catalog:' version: 5.8.2 - apps/event-worker: - dependencies: - '@aws-sdk/client-ec2': - specifier: ^3.701.0 - version: 3.843.0 - '@aws-sdk/client-eks': - specifier: ^3.699.0 - version: 3.840.0 - '@aws-sdk/client-sts': - specifier: ^3.699.0 - version: 3.840.0 - '@azure/arm-containerservice': - specifier: ^21.3.0 - version: 21.6.0 - '@azure/identity': - specifier: ^4.5.0 - version: 4.10.2 - '@ctrlplane/db': - specifier: workspace:* - version: link:../../packages/db - '@ctrlplane/events': - specifier: workspace:* - version: link:../../packages/events - '@ctrlplane/job-dispatch': - specifier: workspace:* - version: link:../../packages/job-dispatch - '@ctrlplane/logger': - specifier: workspace:* - version: link:../../packages/logger - '@ctrlplane/rule-engine': - specifier: workspace:* - version: link:../../packages/rule-engine - '@ctrlplane/validators': - specifier: workspace:* - version: link:../../packages/validators - '@google-cloud/compute': - specifier: ^4.9.0 - version: 4.12.0 - '@google-cloud/container': - specifier: ^5.16.0 - version: 5.19.0 - '@kubernetes/client-node': - specifier: ^0.22.0 - version: 0.22.3 - '@octokit/auth-app': - specifier: 'catalog:' - version: 7.1.5 - '@octokit/rest': - specifier: 'catalog:' - version: 20.1.1 - '@opentelemetry/api': - specifier: ^1.9.0 - version: 1.9.0 - '@opentelemetry/auto-instrumentations-node': - specifier: ^0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-http': - specifier: ^0.54.2 - version: 0.54.2(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-http': - specifier: ^0.54.2 - version: 0.54.2(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': - specifier: ^1.27.0 - version: 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': - specifier: ^0.54.2 - version: 0.54.2(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-node': - specifier: ^0.54.2 - version: 0.54.2(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': - specifier: ^1.27.0 - version: 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': - specifier: ^1.27.0 - version: 1.34.0 - '@smithy/types': - specifier: ^3.7.1 - version: 3.7.2 - '@t3-oss/env-core': - specifier: 'catalog:' - version: 0.11.1(typescript@5.8.2)(zod@3.24.2) - bullmq: - specifier: 'catalog:' - version: 5.48.1 - cron: - specifier: ^3.1.7 - version: 3.5.0 - dotenv: - specifier: ^16.4.5 - version: 16.6.1 - google-auth-library: - specifier: ^9.13.0 - version: 9.15.1 - ioredis: - specifier: 'catalog:' - version: 5.4.1 - js-yaml: - specifier: ^4.1.0 - version: 4.1.0 - lodash: - specifier: 'catalog:' - version: 4.17.21 - ms: - specifier: 'catalog:' - version: 2.1.3 - redis-semaphore: - specifier: 'catalog:' - version: 5.6.2(ioredis@5.4.1) - semver: - specifier: 'catalog:' - version: 7.7.1 - ts-is-present: - specifier: ^1.2.2 - version: 1.2.2 - uuid: - specifier: ^10.0.0 - version: 10.0.0 - zod: - specifier: 'catalog:' - version: 3.24.2 - devDependencies: - '@ctrlplane/eslint-config': - specifier: workspace:^ - version: link:../../tooling/eslint - '@ctrlplane/prettier-config': - specifier: workspace:^ - version: link:../../tooling/prettier - '@ctrlplane/tsconfig': - specifier: workspace:* - version: link:../../tooling/typescript - '@types/js-yaml': - specifier: ^4.0.9 - version: 4.0.9 - '@types/lodash': - specifier: 'catalog:' - version: 4.17.12 - '@types/ms': - specifier: 'catalog:' - version: 0.7.34 - '@types/node': - specifier: catalog:node22 - version: 22.13.10 - '@types/semver': - specifier: ^7.5.8 - version: 7.7.0 - '@types/uuid': - specifier: ^10.0.0 - version: 10.0.0 - eslint: - specifier: 'catalog:' - version: 9.11.1(jiti@2.3.3) - prettier: - specifier: 'catalog:' - version: 3.5.3 - tsx: - specifier: 'catalog:' - version: 4.19.2 - typescript: - specifier: 'catalog:' - version: 5.8.2 - apps/jobs: dependencies: '@ctrlplane/db': @@ -1037,6 +865,9 @@ importers: '@aws-sdk/client-iam': specifier: ^3.696.0 version: 3.840.0 + '@aws-sdk/client-sts': + specifier: ^3.696.0 + version: 3.911.0 '@ctrlplane/auth': specifier: workspace:* version: link:../auth @@ -1246,9 +1077,6 @@ importers: drizzle-zod: specifier: ^0.5.1 version: 0.5.1(drizzle-orm@0.37.0(@libsql/client-wasm@0.15.3)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(@types/react@19.0.8)(pg@8.16.3)(react@19.0.0))(zod@3.24.2) - ioredis: - specifier: 'catalog:' - version: 5.4.1 lodash: specifier: 'catalog:' version: 4.17.21 @@ -1258,9 +1086,6 @@ importers: pg: specifier: ^8.14.1 version: 8.16.3 - redis-semaphore: - specifier: 'catalog:' - version: 5.6.2(ioredis@5.4.1) rrule: specifier: ^2.8.1 version: 2.8.1 @@ -1386,18 +1211,9 @@ importers: '@t3-oss/env-core': specifier: 'catalog:' version: 0.11.1(typescript@5.8.2)(zod@3.24.2) - bullmq: - specifier: 'catalog:' - version: 5.48.1 - bullmq-otel: - specifier: ^1.0.1 - version: 1.0.1 date-fns: specifier: ^4.1.0 version: 4.1.0 - ioredis: - specifier: 'catalog:' - version: 5.4.1 kafkajs: specifier: ^2.2.4 version: 2.2.4 @@ -2164,14 +1980,6 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-ec2@3.843.0': - resolution: {integrity: sha512-HgrSoMi5GSAvXtzmDhkfEwaj1Ei3kNssnkn7aW5rkE68B6GO6ACJq6vEY1yiVb/6OitGf0huNLJF8TDYgt1Jjw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-eks@3.840.0': - resolution: {integrity: sha512-KMjUqu2RzZbkN1JsxF39B0pC/y78Po7ZSyVKdFD6p4LvNIWSmnWWiT47OIvFNj3A2VN92jMRoVk/GcnJq7z73g==} - engines: {node: '>=18.0.0'} - '@aws-sdk/client-iam@3.840.0': resolution: {integrity: sha512-+HWqpTwXQYhFzgwfjGFHfo+a0mRQwYq29BEYlgfcydo8UOApc1oxsVmEmnYh2nbukaefUkOaMDb1xORybsE6Lw==} engines: {node: '>=18.0.0'} @@ -2180,84 +1988,148 @@ packages: resolution: {integrity: sha512-3Zp+FWN2hhmKdpS0Ragi5V2ZPsZNScE3jlbgoJjzjI/roHZqO+e3/+XFN4TlM0DsPKYJNp+1TAjmhxN6rOnfYA==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sts@3.840.0': - resolution: {integrity: sha512-h+mu89Wk81Ne+B624GT/pBM5VjuAZueSeQNixhgtQ1QHi6bZzrpz8+lvMSibKO+kXFyQsTLzkyibbxnhLpWQZA==} + '@aws-sdk/client-sso@3.911.0': + resolution: {integrity: sha512-N9QAeMvN3D1ZyKXkQp4aUgC4wUMuA5E1HuVCkajc0bq1pnH4PIke36YlrDGGREqPlyLFrXCkws2gbL5p23vtlg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-sts@3.911.0': + resolution: {integrity: sha512-mWHTJMeHG4iiCDVdeuVrPNMgbQeKYTe5sp3seL8at+5Y5VfPrHFFLh91IBGiU59OY3GnTajSCoEZl7nsaQD/mw==} engines: {node: '>=18.0.0'} '@aws-sdk/core@3.840.0': resolution: {integrity: sha512-x3Zgb39tF1h2XpU+yA4OAAQlW6LVEfXNlSedSYJ7HGKXqA/E9h3rWQVpYfhXXVVsLdYXdNw5KBUkoAoruoZSZA==} engines: {node: '>=18.0.0'} + '@aws-sdk/core@3.911.0': + resolution: {integrity: sha512-k4QG9A+UCq/qlDJFmjozo6R0eXXfe++/KnCDMmajehIE9kh+b/5DqlGvAmbl9w4e92LOtrY6/DN3mIX1xs4sXw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-env@3.840.0': resolution: {integrity: sha512-EzF6VcJK7XvQ/G15AVEfJzN2mNXU8fcVpXo4bRyr1S6t2q5zx6UPH/XjDbn18xyUmOq01t+r8gG+TmHEVo18fA==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-env@3.911.0': + resolution: {integrity: sha512-6FWRwWn3LUZzLhqBXB+TPMW2ijCWUqGICSw8bVakEdODrvbiv1RT/MVUayzFwz/ek6e6NKZn6DbSWzx07N9Hjw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-http@3.840.0': resolution: {integrity: sha512-wbnUiPGLVea6mXbUh04fu+VJmGkQvmToPeTYdHE8eRZq3NRDi3t3WltT+jArLBKD/4NppRpMjf2ju4coMCz91g==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-http@3.911.0': + resolution: {integrity: sha512-xUlwKmIUW2fWP/eM3nF5u4CyLtOtyohlhGJ5jdsJokr3MrQ7w0tDITO43C9IhCn+28D5UbaiWnKw5ntkw7aVfA==} + engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-ini@3.840.0': resolution: {integrity: sha512-7F290BsWydShHb+7InXd+IjJc3mlEIm9I0R57F/Pjl1xZB69MdkhVGCnuETWoBt4g53ktJd6NEjzm/iAhFXFmw==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-ini@3.911.0': + resolution: {integrity: sha512-bQ86kWAZ0Imn7uWl7uqOYZ2aqlkftPmEc8cQh+QyhmUXbia8II4oYKq/tMek6j3M5UOMCiJVxzJoxemJZA6/sw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-node@3.840.0': resolution: {integrity: sha512-KufP8JnxA31wxklLm63evUPSFApGcH8X86z3mv9SRbpCm5ycgWIGVCTXpTOdgq6rPZrwT9pftzv2/b4mV/9clg==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-node@3.911.0': + resolution: {integrity: sha512-4oGpLwgQCKNtVoJROztJ4v7lZLhCqcUMX6pe/DQ2aU0TktZX7EczMCIEGjVo5b7yHwSNWt2zW0tDdgVUTsMHPw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-process@3.840.0': resolution: {integrity: sha512-HkDQWHy8tCI4A0Ps2NVtuVYMv9cB4y/IuD/TdOsqeRIAT12h8jDb98BwQPNLAImAOwOWzZJ8Cu0xtSpX7CQhMw==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-process@3.911.0': + resolution: {integrity: sha512-mKshhV5jRQffZjbK9x7bs+uC2IsYKfpzYaBamFsEov3xtARCpOiKaIlM8gYKFEbHT2M+1R3rYYlhhl9ndVWS2g==} + engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-sso@3.840.0': resolution: {integrity: sha512-2qgdtdd6R0Z1y0KL8gzzwFUGmhBHSUx4zy85L2XV1CXhpRNwV71SVWJqLDVV5RVWVf9mg50Pm3AWrUC0xb0pcA==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-sso@3.911.0': + resolution: {integrity: sha512-JAxd4uWe0Zc9tk6+N0cVxe9XtJVcOx6Ms0k933ZU9QbuRMH6xti/wnZxp/IvGIWIDzf5fhqiGyw5MSyDeI5b1w==} + engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-web-identity@3.840.0': resolution: {integrity: sha512-dpEeVXG8uNZSmVXReE4WP0lwoioX2gstk4RnUgrdUE3YaPq8A+hJiVAyc3h+cjDeIqfbsQbZm9qFetKC2LF9dQ==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-web-identity@3.911.0': + resolution: {integrity: sha512-urIbXWWG+cm54RwwTFQuRwPH0WPsMFSDF2/H9qO2J2fKoHRURuyblFCyYG3aVKZGvFBhOizJYexf5+5w3CJKBw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-host-header@3.840.0': resolution: {integrity: sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-host-header@3.910.0': + resolution: {integrity: sha512-F9Lqeu80/aTM6S/izZ8RtwSmjfhWjIuxX61LX+/9mxJyEkgaECRxv0chsLQsLHJumkGnXRy/eIyMLBhcTPF5vg==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-logger@3.840.0': resolution: {integrity: sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-logger@3.910.0': + resolution: {integrity: sha512-3LJyyfs1USvRuRDla1pGlzGRtXJBXD1zC9F+eE9Iz/V5nkmhyv52A017CvKWmYoR0DM9dzjLyPOI0BSSppEaTw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-recursion-detection@3.840.0': resolution: {integrity: sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-ec2@3.840.0': - resolution: {integrity: sha512-TVYRq3NNq+Cb4N5jODASOmKwPBa4zXH0CT5Ifrav+fH7SVtkfXurVMkLaAu1zFHyllQgAQ6O4O/MpwDq2H1nkw==} + '@aws-sdk/middleware-recursion-detection@3.910.0': + resolution: {integrity: sha512-m/oLz0EoCy+WoIVBnXRXJ4AtGpdl0kPE7U+VH9TsuUzHgxY1Re/176Q1HWLBRVlz4gr++lNsgsMWEC+VnAwMpw==} engines: {node: '>=18.0.0'} '@aws-sdk/middleware-user-agent@3.840.0': resolution: {integrity: sha512-hiiMf7BP5ZkAFAvWRcK67Mw/g55ar7OCrvrynC92hunx/xhMkrgSLM0EXIZ1oTn3uql9kH/qqGF0nqsK6K555A==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-user-agent@3.911.0': + resolution: {integrity: sha512-rY3LvGvgY/UI0nmt5f4DRzjEh8135A2TeHcva1bgOmVfOI4vkkGfA20sNRqerOkSO6hPbkxJapO50UJHFzmmyA==} + engines: {node: '>=18.0.0'} + '@aws-sdk/nested-clients@3.840.0': resolution: {integrity: sha512-LXYYo9+n4hRqnRSIMXLBb+BLz+cEmjMtTudwK1BF6Bn2RfdDv29KuyeDRrPCS3TwKl7ZKmXUmE9n5UuHAPfBpA==} engines: {node: '>=18.0.0'} + '@aws-sdk/nested-clients@3.911.0': + resolution: {integrity: sha512-lp/sXbdX/S0EYaMYPVKga0omjIUbNNdFi9IJITgKZkLC6CzspihIoHd5GIdl4esMJevtTQQfkVncXTFkf/a4YA==} + engines: {node: '>=18.0.0'} + '@aws-sdk/region-config-resolver@3.840.0': resolution: {integrity: sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==} engines: {node: '>=18.0.0'} + '@aws-sdk/region-config-resolver@3.910.0': + resolution: {integrity: sha512-gzQAkuHI3xyG6toYnH/pju+kc190XmvnB7X84vtN57GjgdQJICt9So/BD0U6h+eSfk9VBnafkVrAzBzWMEFZVw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/token-providers@3.840.0': resolution: {integrity: sha512-6BuTOLTXvmgwjK7ve7aTg9JaWFdM5UoMolLVPMyh3wTv9Ufalh8oklxYHUBIxsKkBGO2WiHXytveuxH6tAgTYg==} engines: {node: '>=18.0.0'} + '@aws-sdk/token-providers@3.911.0': + resolution: {integrity: sha512-O1c5F1pbEImgEe3Vr8j1gpWu69UXWj3nN3vvLGh77hcrG5dZ8I27tSP5RN4Labm8Dnji/6ia+vqSYpN8w6KN5A==} + engines: {node: '>=18.0.0'} + '@aws-sdk/types@3.840.0': resolution: {integrity: sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==} engines: {node: '>=18.0.0'} + '@aws-sdk/types@3.910.0': + resolution: {integrity: sha512-o67gL3vjf4nhfmuSUNNkit0d62QJEwwHLxucwVJkR/rw9mfUtAWsgBs8Tp16cdUbMgsyQtCQilL8RAJDoGtadQ==} + engines: {node: '>=18.0.0'} + '@aws-sdk/util-endpoints@3.840.0': resolution: {integrity: sha512-eqE9ROdg/Kk0rj3poutyRCFauPDXIf/WSvCqFiRDDVi6QOnCv/M0g2XW8/jSvkJlOyaXkNCptapIp6BeeFFGYw==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-format-url@3.840.0': - resolution: {integrity: sha512-VB1PWyI1TQPiPvg4w7tgUGGQER1xxXPNUqfh3baxUSFi1Oh8wHrDnFywkxLm3NMmgDmnLnSZ5Q326qAoyqKLSg==} + '@aws-sdk/util-endpoints@3.910.0': + resolution: {integrity: sha512-6XgdNe42ibP8zCQgNGDWoOF53RfEKzpU/S7Z29FTTJ7hcZv0SytC0ZNQQZSx4rfBl036YWYwJRoJMlT4AA7q9A==} engines: {node: '>=18.0.0'} '@aws-sdk/util-locate-window@3.693.0': @@ -2267,6 +2139,9 @@ packages: '@aws-sdk/util-user-agent-browser@3.840.0': resolution: {integrity: sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==} + '@aws-sdk/util-user-agent-browser@3.910.0': + resolution: {integrity: sha512-iOdrRdLZHrlINk9pezNZ82P/VxO/UmtmpaOAObUN+xplCUJu31WNM2EE/HccC8PQw6XlAudpdA6HDTGiW6yVGg==} + '@aws-sdk/util-user-agent-node@3.840.0': resolution: {integrity: sha512-Fy5JUEDQU1tPm2Yw/YqRYYc27W5+QD/J4mYvQvdWjUGZLB5q3eLFMGD35Uc28ZFoGMufPr4OCxK/bRfWROBRHQ==} engines: {node: '>=18.0.0'} @@ -2276,82 +2151,27 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.821.0': - resolution: {integrity: sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==} - engines: {node: '>=18.0.0'} - - '@azure/abort-controller@2.1.2': - resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} - engines: {node: '>=18.0.0'} - - '@azure/arm-containerservice@21.6.0': - resolution: {integrity: sha512-DrelRh6wzeGAZsCKNLBA02LSzSgyD7jCaIzZ33Iy3nTvpYjuaISSTJyTCcf1QOCclUBUS19Pg2TbBG6KxMOuQg==} - engines: {node: '>=18.0.0'} - - '@azure/core-auth@1.9.0': - resolution: {integrity: sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==} - engines: {node: '>=18.0.0'} - - '@azure/core-client@1.9.2': - resolution: {integrity: sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==} - engines: {node: '>=18.0.0'} - - '@azure/core-client@1.9.4': - resolution: {integrity: sha512-f7IxTD15Qdux30s2qFARH+JxgwxWLG2Rlr4oSkPGuLWm+1p5y1+C04XGLA0vmX6EtqfutmjvpNmAfgwVIS5hpw==} - engines: {node: '>=18.0.0'} - - '@azure/core-lro@2.7.2': - resolution: {integrity: sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==} - engines: {node: '>=18.0.0'} - - '@azure/core-paging@1.6.2': - resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} - engines: {node: '>=18.0.0'} - - '@azure/core-rest-pipeline@1.18.1': - resolution: {integrity: sha512-/wS73UEDrxroUEVywEm7J0p2c+IIiVxyfigCGfsKvCxxCET4V/Hef2aURqltrXMRjNmdmt5IuOgIpl8f6xdO5A==} - engines: {node: '>=18.0.0'} - - '@azure/core-rest-pipeline@1.21.0': - resolution: {integrity: sha512-a4MBwe/5WKbq9MIxikzgxLBbruC5qlkFYlBdI7Ev50Y7ib5Vo/Jvt5jnJo7NaWeJ908LCHL0S1Us4UMf1VoTfg==} - engines: {node: '>=18.0.0'} - - '@azure/core-tracing@1.2.0': - resolution: {integrity: sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==} - engines: {node: '>=18.0.0'} - - '@azure/core-util@1.11.0': - resolution: {integrity: sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==} + '@aws-sdk/util-user-agent-node@3.911.0': + resolution: {integrity: sha512-3l+f6ooLF6Z6Lz0zGi7vSKSUYn/EePPizv88eZQpEAFunBHv+CSVNPtxhxHfkm7X9tTsV4QGZRIqo3taMLolmA==} engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true - '@azure/core-util@1.12.0': - resolution: {integrity: sha512-13IyjTQgABPARvG90+N2dXpC+hwp466XCdQXPCRlbWHgd3SJd5Q1VvaBGv6k1BIa4MQm6hAF1UBU1m8QUxV8sQ==} + '@aws-sdk/xml-builder@3.821.0': + resolution: {integrity: sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==} engines: {node: '>=18.0.0'} - '@azure/identity@4.10.2': - resolution: {integrity: sha512-Uth4vz0j+fkXCkbvutChUj03PDCokjbC6Wk9JT8hHEUtpy/EurNKAseb3+gO6Zi9VYBvwt61pgbzn1ovk942Qg==} - engines: {node: '>=20.0.0'} - - '@azure/logger@1.1.4': - resolution: {integrity: sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==} + '@aws-sdk/xml-builder@3.911.0': + resolution: {integrity: sha512-/yh3oe26bZfCVGrIMRM9Z4hvvGJD+qx5tOLlydOkuBkm72aXON7D9+MucjJXTAcI8tF2Yq+JHa0478eHQOhnLg==} engines: {node: '>=18.0.0'} - '@azure/logger@1.2.0': - resolution: {integrity: sha512-0hKEzLhpw+ZTAfNJyRrn6s+V0nDWzXk9OjBr2TiGIu0OfMr5s2V4FpKLTAK3Ca5r5OKLbf4hkOGDPyiRjie/jA==} + '@aws/lambda-invoke-store@0.0.1': + resolution: {integrity: sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==} engines: {node: '>=18.0.0'} - '@azure/msal-browser@4.15.0': - resolution: {integrity: sha512-+AIGTvpVz+FIx5CsM1y+nW0r/qOb/ChRdM8/Cbp+jKWC0Wdw4ldnwPdYOBi5NaALUQnYITirD9XMZX7LdklEzQ==} - engines: {node: '>=0.8.0'} - - '@azure/msal-common@15.8.1': - resolution: {integrity: sha512-ltIlFK5VxeJ5BurE25OsJIfcx1Q3H/IZg2LjV9d4vmH+5t4c1UCyRQ/HgKLgXuCZShs7qfc/TC95GYZfsUsJUQ==} - engines: {node: '>=0.8.0'} - - '@azure/msal-node@3.6.3': - resolution: {integrity: sha512-95wjsKGyUcAd5tFmQBo5Ug/kOj+hFh/8FsXuxluEvdfbgg6xCimhSP9qnyq6+xIg78/jREkBD1/BSqd7NIDDYQ==} - engines: {node: '>=16'} - '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} @@ -3511,27 +3331,10 @@ packages: '@formatjs/intl-localematcher@0.6.1': resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==} - '@google-cloud/compute@4.12.0': - resolution: {integrity: sha512-N3isWbxIMd02qzdlFFxHxEM+2B/vNgn9N7WMpteY2sfwN1yT9PJHcilKDLyw+uIwuQAoErNxBM+JOLq3r/Tv+Q==} - engines: {node: '>=14.0.0'} - - '@google-cloud/container@5.19.0': - resolution: {integrity: sha512-1gPAZbID65XVpQhi7nmE8BednUuxomsphc6wrkzjkq3AQqMKhnqj8Uhn4hfJgT26DSQ5IPPS9PVECS2HHV13EA==} - engines: {node: '>=14.0.0'} - - '@grpc/grpc-js@1.11.3': - resolution: {integrity: sha512-i9UraDzFHMR+Iz/MhFLljT+fCpgxZ3O6CxwGJ8YuNYHJItIHUzKJpW2LvoFZNnGPwqc9iWy9RAucxV0JoR9aUQ==} - engines: {node: '>=12.10.0'} - '@grpc/grpc-js@1.14.0': resolution: {integrity: sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==} engines: {node: '>=12.10.0'} - '@grpc/proto-loader@0.7.13': - resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} - engines: {node: '>=6'} - hasBin: true - '@grpc/proto-loader@0.8.0': resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} engines: {node: '>=6'} @@ -3681,9 +3484,6 @@ packages: '@internationalized/string@3.2.7': resolution: {integrity: sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A==} - '@ioredis/commands@1.2.0': - resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} - '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -3785,36 +3585,6 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} - cpu: [arm64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} - cpu: [x64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} - cpu: [arm64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} - cpu: [arm] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} - cpu: [x64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} - cpu: [x64] - os: [win32] - '@nestjs/axios@4.0.0': resolution: {integrity: sha512-1cB+Jyltu/uUPNQrpUimRHEQHrnQrpLzVj6dU3dgn6iDDDdahr10TgHFGTmw5VuJ9GzKZsCLDL78VSwJAs/9JQ==} peerDependencies: @@ -6062,10 +5832,22 @@ packages: resolution: {integrity: sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==} engines: {node: '>=18.0.0'} + '@smithy/abort-controller@4.2.3': + resolution: {integrity: sha512-xWL9Mf8b7tIFuAlpjKtRPnHrR8XVrwTj5NPYO/QwZPtc0SDLsPxb56V5tzi5yspSMytISHybifez+4jlrx0vkQ==} + engines: {node: '>=18.0.0'} + '@smithy/config-resolver@4.1.4': resolution: {integrity: sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==} engines: {node: '>=18.0.0'} + '@smithy/config-resolver@4.3.3': + resolution: {integrity: sha512-xSql8A1Bl41O9JvGU/CtgiLBlwkvpHTSKRlvz9zOBvBCPjXghZ6ZkcVzmV2f7FLAA+80+aqKmIOmy8pEDrtCaw==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.17.0': + resolution: {integrity: sha512-Tir3DbfoTO97fEGUZjzGeoXgcQAUBRDTmuH9A8lxuP8ATrgezrAJ6cLuRvwdKN4ZbYNlHgKlBX69Hyu3THYhtg==} + engines: {node: '>=18.0.0'} + '@smithy/core@3.6.0': resolution: {integrity: sha512-Pgvfb+TQ4wUNLyHzvgCP4aYZMh16y7GcfF59oirRHcgGgkH1e/s9C0nv/v3WP+Quymyr5je71HeFQCwh+44XLg==} engines: {node: '>=18.0.0'} @@ -6074,19 +5856,35 @@ packages: resolution: {integrity: sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==} engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.2.3': + resolution: {integrity: sha512-hA1MQ/WAHly4SYltJKitEsIDVsNmXcQfYBRv2e+q04fnqtAX5qXaybxy/fhUeAMCnQIdAjaGDb04fMHQefWRhw==} + engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.0.4': resolution: {integrity: sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.3.4': + resolution: {integrity: sha512-bwigPylvivpRLCm+YK9I5wRIYjFESSVwl8JQ1vVx/XhCw0PtCi558NwTnT2DaVCl5pYlImGuQTSwMsZ+pIavRw==} + engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.0.4': resolution: {integrity: sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==} engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.2.3': + resolution: {integrity: sha512-6+NOdZDbfuU6s1ISp3UOk5Rg953RJ2aBLNLLBEcamLjHAg1Po9Ha7QIB5ZWhdRUVuOUrT8BVFR+O2KIPmw027g==} + engines: {node: '>=18.0.0'} + '@smithy/invalid-dependency@4.0.4': resolution: {integrity: sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==} engines: {node: '>=18.0.0'} - '@smithy/is-array-buffer@2.2.0': + '@smithy/invalid-dependency@4.2.3': + resolution: {integrity: sha512-Cc9W5DwDuebXEDMpOpl4iERo8I0KFjTnomK2RMdhhR87GwrSmUmwMxS4P5JdRf+LsjOdIqumcerwRgYMr/tZ9Q==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} @@ -6094,90 +5892,170 @@ packages: resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} engines: {node: '>=18.0.0'} + '@smithy/is-array-buffer@4.2.0': + resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-content-length@4.0.4': resolution: {integrity: sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==} engines: {node: '>=18.0.0'} + '@smithy/middleware-content-length@4.2.3': + resolution: {integrity: sha512-/atXLsT88GwKtfp5Jr0Ks1CSa4+lB+IgRnkNrrYP0h1wL4swHNb0YONEvTceNKNdZGJsye+W2HH8W7olbcPUeA==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.1.13': resolution: {integrity: sha512-xg3EHV/Q5ZdAO5b0UiIMj3RIOCobuS40pBBODguUDVdko6YK6QIzCVRrHTogVuEKglBWqWenRnZ71iZnLL3ZAQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.3.4': + resolution: {integrity: sha512-/RJhpYkMOaUZoJEkddamGPPIYeKICKXOu/ojhn85dKDM0n5iDIhjvYAQLP3K5FPhgB203O3GpWzoK2OehEoIUw==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.1.14': resolution: {integrity: sha512-eoXaLlDGpKvdmvt+YBfRXE7HmIEtFF+DJCbTPwuLunP0YUnrydl+C4tS+vEM0+nyxXrX3PSUFqC+lP1+EHB1Tw==} engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.4.4': + resolution: {integrity: sha512-vSgABQAkuUHRO03AhR2rWxVQ1un284lkBn+NFawzdahmzksAoOeVMnXXsuPViL4GlhRHXqFaMlc8Mj04OfQk1w==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.0.8': resolution: {integrity: sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==} engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.2.3': + resolution: {integrity: sha512-8g4NuUINpYccxiCXM5s1/V+uLtts8NcX4+sPEbvYQDZk4XoJfDpq5y2FQxfmUL89syoldpzNzA0R9nhzdtdKnQ==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@4.0.4': resolution: {integrity: sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==} engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@4.2.3': + resolution: {integrity: sha512-iGuOJkH71faPNgOj/gWuEGS6xvQashpLwWB1HjHq1lNNiVfbiJLpZVbhddPuDbx9l4Cgl0vPLq5ltRfSaHfspA==} + engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@4.1.3': resolution: {integrity: sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==} engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@4.3.3': + resolution: {integrity: sha512-NzI1eBpBSViOav8NVy1fqOlSfkLgkUjUTlohUSgAEhHaFWA3XJiLditvavIP7OpvTjDp5u2LhtlBhkBlEisMwA==} + engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.0.6': resolution: {integrity: sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==} engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.4.2': + resolution: {integrity: sha512-MHFvTjts24cjGo1byXqhXrbqm7uznFD/ESFx8npHMWTFQVdBZjrT1hKottmp69LBTRm/JQzP/sn1vPt0/r6AYQ==} + engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.0.4': resolution: {integrity: sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==} engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.2.3': + resolution: {integrity: sha512-+1EZ+Y+njiefCohjlhyOcy1UNYjT+1PwGFHCxA/gYctjg3DQWAU19WigOXAco/Ql8hZokNehpzLd0/+3uCreqQ==} + engines: {node: '>=18.0.0'} + '@smithy/protocol-http@5.1.2': resolution: {integrity: sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==} engines: {node: '>=18.0.0'} + '@smithy/protocol-http@5.3.3': + resolution: {integrity: sha512-Mn7f/1aN2/jecywDcRDvWWWJF4uwg/A0XjFMJtj72DsgHTByfjRltSqcT9NyE9RTdBSN6X1RSXrhn/YWQl8xlw==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@4.0.4': resolution: {integrity: sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==} engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@4.2.3': + resolution: {integrity: sha512-LOVCGCmwMahYUM/P0YnU/AlDQFjcu+gWbFJooC417QRB/lDJlWSn8qmPSDp+s4YVAHOgtgbNG4sR+SxF/VOcJQ==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@4.0.4': resolution: {integrity: sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==} engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@4.2.3': + resolution: {integrity: sha512-cYlSNHcTAX/wc1rpblli3aUlLMGgKZ/Oqn8hhjFASXMCXjIqeuQBei0cnq2JR8t4RtU9FpG6uyl6PxyArTiwKA==} + engines: {node: '>=18.0.0'} + '@smithy/service-error-classification@4.0.6': resolution: {integrity: sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==} engines: {node: '>=18.0.0'} + '@smithy/service-error-classification@4.2.3': + resolution: {integrity: sha512-NkxsAxFWwsPsQiwFG2MzJ/T7uIR6AQNh1SzcxSUnmmIqIQMlLRQDKhc17M7IYjiuBXhrQRjQTo3CxX+DobS93g==} + engines: {node: '>=18.0.0'} + '@smithy/shared-ini-file-loader@4.0.4': resolution: {integrity: sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==} engines: {node: '>=18.0.0'} + '@smithy/shared-ini-file-loader@4.3.3': + resolution: {integrity: sha512-9f9Ixej0hFhroOK2TxZfUUDR13WVa8tQzhSzPDgXe5jGL3KmaM9s8XN7RQwqtEypI82q9KHnKS71CJ+q/1xLtQ==} + engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.1.2': resolution: {integrity: sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==} engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.3.3': + resolution: {integrity: sha512-CmSlUy+eEYbIEYN5N3vvQTRfqt0lJlQkaQUIf+oizu7BbDut0pozfDjBGecfcfWf7c62Yis4JIEgqQ/TCfodaA==} + engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.4.5': resolution: {integrity: sha512-+lynZjGuUFJaMdDYSTMnP/uPBBXXukVfrJlP+1U/Dp5SFTEI++w6NMga8DjOENxecOF71V9Z2DllaVDYRnGlkg==} engines: {node: '>=18.0.0'} - '@smithy/types@3.7.2': - resolution: {integrity: sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@4.9.0': + resolution: {integrity: sha512-qz7RTd15GGdwJ3ZCeBKLDQuUQ88m+skh2hJwcpPm1VqLeKzgZvXf6SrNbxvx7uOqvvkjCMXqx3YB5PDJyk00ww==} + engines: {node: '>=18.0.0'} '@smithy/types@4.3.1': resolution: {integrity: sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==} engines: {node: '>=18.0.0'} + '@smithy/types@4.8.0': + resolution: {integrity: sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ==} + engines: {node: '>=18.0.0'} + '@smithy/url-parser@4.0.4': resolution: {integrity: sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==} engines: {node: '>=18.0.0'} + '@smithy/url-parser@4.2.3': + resolution: {integrity: sha512-I066AigYvY3d9VlU3zG9XzZg1yT10aNqvCaBTw9EPgu5GrsEl1aUkcMvhkIXascYH1A8W0LQo3B1Kr1cJNcQEw==} + engines: {node: '>=18.0.0'} + '@smithy/util-base64@4.0.0': resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} engines: {node: '>=18.0.0'} + '@smithy/util-base64@4.3.0': + resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-body-length-browser@4.0.0': resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} engines: {node: '>=18.0.0'} + '@smithy/util-body-length-browser@4.2.0': + resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} + engines: {node: '>=18.0.0'} + '@smithy/util-body-length-node@4.0.0': resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} engines: {node: '>=18.0.0'} + '@smithy/util-body-length-node@4.2.1': + resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} + 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'} @@ -6186,42 +6064,82 @@ packages: resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} engines: {node: '>=18.0.0'} + '@smithy/util-buffer-from@4.2.0': + resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + engines: {node: '>=18.0.0'} + '@smithy/util-config-provider@4.0.0': resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} engines: {node: '>=18.0.0'} + '@smithy/util-config-provider@4.2.0': + resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.0.21': resolution: {integrity: sha512-wM0jhTytgXu3wzJoIqpbBAG5U6BwiubZ6QKzSbP7/VbmF1v96xlAbX2Am/mz0Zep0NLvLh84JT0tuZnk3wmYQA==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.3.3': + resolution: {integrity: sha512-vqHoybAuZXbFXZqgzquiUXtdY+UT/aU33sxa4GBPkiYklmR20LlCn+d3Wc3yA5ZM13gQ92SZe/D8xh6hkjx+IQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.0.21': resolution: {integrity: sha512-/F34zkoU0GzpUgLJydHY8Rxu9lBn8xQC/s/0M0U9lLBkYbA1htaAFjWYJzpzsbXPuri5D1H8gjp2jBum05qBrA==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.2.4': + resolution: {integrity: sha512-X5/xrPHedifo7hJUUWKlpxVb2oDOiqPUXlvsZv1EZSjILoutLiJyWva3coBpn00e/gPSpH8Rn2eIbgdwHQdW7Q==} + engines: {node: '>=18.0.0'} + '@smithy/util-endpoints@3.0.6': resolution: {integrity: sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==} engines: {node: '>=18.0.0'} + '@smithy/util-endpoints@3.2.3': + resolution: {integrity: sha512-aCfxUOVv0CzBIkU10TubdgKSx5uRvzH064kaiPEWfNIvKOtNpu642P4FP1hgOFkjQIkDObrfIDnKMKkeyrejvQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-hex-encoding@4.0.0': resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} engines: {node: '>=18.0.0'} + '@smithy/util-hex-encoding@4.2.0': + resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + engines: {node: '>=18.0.0'} + '@smithy/util-middleware@4.0.4': resolution: {integrity: sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==} engines: {node: '>=18.0.0'} + '@smithy/util-middleware@4.2.3': + resolution: {integrity: sha512-v5ObKlSe8PWUHCqEiX2fy1gNv6goiw6E5I/PN2aXg3Fb/hse0xeaAnSpXDiWl7x6LamVKq7senB+m5LOYHUAHw==} + engines: {node: '>=18.0.0'} + '@smithy/util-retry@4.0.6': resolution: {integrity: sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==} engines: {node: '>=18.0.0'} + '@smithy/util-retry@4.2.3': + resolution: {integrity: sha512-lLPWnakjC0q9z+OtiXk+9RPQiYPNAovt2IXD3CP4LkOnd9NpUsxOjMx1SnoUVB7Orb7fZp67cQMtTBKMFDvOGg==} + engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.2.2': resolution: {integrity: sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==} engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.3': + resolution: {integrity: sha512-oZvn8a5bwwQBNYHT2eNo0EU8Kkby3jeIg1P2Lu9EQtqDxki1LIjGRJM6dJ5CZUig8QmLxWxqOKWvg3mVoOBs5A==} + engines: {node: '>=18.0.0'} + '@smithy/util-uri-escape@4.0.0': resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} engines: {node: '>=18.0.0'} + '@smithy/util-uri-escape@4.2.0': + resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} + engines: {node: '>=18.0.0'} + '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} @@ -6230,10 +6148,18 @@ packages: resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} engines: {node: '>=18.0.0'} + '@smithy/util-utf8@4.2.0': + resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + engines: {node: '>=18.0.0'} + '@smithy/util-waiter@4.0.6': resolution: {integrity: sha512-slcr1wdRbX7NFphXZOxtxRNA7hXAAtJAXJDE/wdoMAos27SIquVCKiSqfB6/28YzQ8FCsB5NKkhdM5gMADbqxg==} engines: {node: '>=18.0.0'} + '@smithy/uuid@1.1.0': + resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} + engines: {node: '>=18.0.0'} + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -6401,10 +6327,6 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@tootallnate/once@2.0.0': - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} @@ -6465,9 +6387,6 @@ packages: '@types/bunyan@1.8.9': resolution: {integrity: sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==} - '@types/caseless@0.12.5': - resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} - '@types/connect@3.4.36': resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} @@ -6623,9 +6542,6 @@ packages: '@types/lodash@4.17.12': resolution: {integrity: sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==} - '@types/long@4.0.2': - resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - '@types/luxon@3.4.2': resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} @@ -6694,9 +6610,6 @@ packages: '@types/react@19.0.8': resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==} - '@types/request@2.48.12': - resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} - '@types/retry@0.12.2': resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} @@ -6724,9 +6637,6 @@ packages: '@types/tinycolor2@1.4.6': resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} - '@types/tough-cookie@4.0.5': - resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} @@ -6742,9 +6652,6 @@ packages: '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -6832,10 +6739,6 @@ packages: resolution: {integrity: sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typespec/ts-http-runtime@0.2.3': - resolution: {integrity: sha512-oRhjSzcVjX8ExyaF8hC0zzTqxlVuRlgMHL/Bh4w3xB9+wjbm0FpXylVU/lBrn+kgphwYTrOk3tp+AVShGmlYCg==} - engines: {node: '>=18.0.0'} - '@vercel/ncc@0.38.3': resolution: {integrity: sha512-rnK6hJBS6mwc+Bkab+PGPs9OiS0i/3kdTO+CkI8V0/VrW3vmz7O2Pxjw/owOlmo6PKEIxRSeZKv/kuL9itnpYA==} hasBin: true @@ -6974,10 +6877,6 @@ packages: add@2.0.6: resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==} - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - agent-base@7.1.1: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} @@ -7268,16 +7167,6 @@ packages: resolution: {integrity: sha512-sWm8iPbqvL9+5SiYxXH73UOkyEbGQg7kyHQmReF89WJHQJw2eV4P/yZ0E+b71cczJ4pPobVhXxgQcmfSTgGHxQ==} engines: {node: '>= 0.10.x'} - bullmq-otel@1.0.1: - resolution: {integrity: sha512-9SSBA/iq8bFUJ9I5bDwv1UmtbJAmIGHt796g5lsYZWasqouTqkFA3+Z/n17mCc8VzfxxygCCCBsXdC1mICbxdw==} - - bullmq@5.48.1: - resolution: {integrity: sha512-WA/NlPwmxgbDsL8KGIkQvGlRkBdVAyRiypP+Witm6Tyd2PwnBQ8K62ifnlA16rF59vpnEKwO+rmh/ZM7gKmDqg==} - - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7436,10 +7325,6 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - cmdk@1.1.1: resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} peerDependencies: @@ -7580,10 +7465,6 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cron-parser@4.9.0: - resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} - engines: {node: '>=12.0.0'} - cron@3.5.0: resolution: {integrity: sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==} @@ -7805,14 +7686,6 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - default-browser-id@5.0.0: - resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} - engines: {node: '>=18'} - - default-browser@5.2.1: - resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} - engines: {node: '>=18'} - defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} @@ -7820,10 +7693,6 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -7840,10 +7709,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - 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'} @@ -8034,9 +7899,6 @@ packages: duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - duplexify@4.1.3: - resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} - eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -8072,9 +7934,6 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - engine.io-parser@5.2.3: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} @@ -8436,6 +8295,10 @@ packages: resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} hasBin: true + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + fastest-stable-stringify@2.0.2: resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} @@ -8523,10 +8386,6 @@ packages: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} engines: {node: '>= 0.12'} - form-data@2.5.3: - resolution: {integrity: sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==} - engines: {node: '>= 0.12'} - form-data@4.0.3: resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} @@ -8704,10 +8563,6 @@ packages: resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} engines: {node: '>=14'} - google-gax@4.4.1: - resolution: {integrity: sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==} - engines: {node: '>=14'} - googleapis-common@7.2.0: resolution: {integrity: sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==} engines: {node: '>=14.0.0'} @@ -8842,10 +8697,6 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} - http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -8858,10 +8709,6 @@ packages: resolution: {integrity: sha512-YQF7j8Qf/Rlby0IbRPiWfNZt6aeUv3K0Pi0x3crbMZN+7F8dPn5k4b3n897vpM1Vk8Mg2fhOYc9fktKEQWMy/Q==} engines: {node: '>= 0.4.0'} - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - https-proxy-agent@7.0.5: resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} @@ -8953,10 +8800,6 @@ packages: invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ioredis@5.4.1: - resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} - engines: {node: '>=12.22.0'} - ip-address@9.0.5: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} @@ -9032,11 +8875,6 @@ packages: is-decimal@1.0.4: resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -9060,11 +8898,6 @@ packages: is-hexadecimal@1.0.4: resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -9191,10 +9024,6 @@ packages: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -9345,10 +9174,6 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - jsonwebtoken@9.0.2: - resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} - engines: {node: '>=12', npm: '>=6'} - jsprim@1.4.2: resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} engines: {node: '>=0.6.0'} @@ -9357,15 +9182,9 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} - jwa@1.4.2: - resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} - jwa@2.0.0: resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} - jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - jws@4.0.0: resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} @@ -9424,40 +9243,16 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. - 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.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==} @@ -9526,10 +9321,6 @@ packages: resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} engines: {node: '>=12'} - luxon@3.6.1: - resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==} - engines: {node: '>=12'} - lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -9660,13 +9451,6 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msgpackr-extract@3.0.3: - resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} - hasBin: true - - msgpackr@1.11.2: - resolution: {integrity: sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==} - murmurhash-js@1.0.0: resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==} @@ -9830,10 +9614,6 @@ packages: encoding: optional: true - node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} - hasBin: true - node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -9934,10 +9714,6 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - open@10.1.2: - resolution: {integrity: sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==} - engines: {node: '>=18'} - openapi-fetch@0.13.8: resolution: {integrity: sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==} @@ -10363,10 +10139,6 @@ packages: property-information@5.6.0: resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} - proto3-json-serializer@2.0.2: - resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} - engines: {node: '>=14.0.0'} - protobufjs@7.4.0: resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} engines: {node: '>=12.0.0'} @@ -10652,23 +10424,6 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - 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-semaphore@5.6.2: - resolution: {integrity: sha512-Oh1zOqNa51VC14mwYcmdOyjHpb+y8N1ieqpGxITjkrqPiO8IoCYiXGrSyKEmXH5+UEsl/7OAnju2e0x1TY5Jhg==} - engines: {node: '>= 14.17.0'} - peerDependencies: - ioredis: ^4.1.0 || ^5 - peerDependenciesMeta: - ioredis: - optional: true - redux-immutable@4.0.0: resolution: {integrity: sha512-SchSn/DWfGb3oAejd+1hhHx01xUoxY+V7TeK0BKqpkLKiQPVFf7DYzEaKmrEVxsWxielKfSK9/Xq66YyxgR1cg==} peerDependencies: @@ -10771,10 +10526,6 @@ packages: resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} engines: {node: '>=4'} - retry-request@7.0.2: - resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==} - engines: {node: '>=14'} - retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -10812,10 +10563,6 @@ packages: rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} - run-applescript@7.0.0: - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} - engines: {node: '>=18'} - run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -11050,6 +10797,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions space-separated-tokens@1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} @@ -11095,9 +10843,6 @@ packages: stacktrace-js@2.0.2: resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} - standard-as-callback@2.1.0: - resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - state-local@1.0.7: resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} @@ -11116,12 +10861,6 @@ packages: resolution: {integrity: sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==} engines: {node: '>= 0.10.0'} - stream-events@1.0.5: - resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} - - stream-shift@1.0.3: - resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} - streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -11194,13 +10933,13 @@ packages: strnum@1.1.2: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + strnum@2.1.1: + resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} + strtok3@10.3.1: resolution: {integrity: sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw==} engines: {node: '>=18'} - stubs@3.0.0: - resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} - styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -11297,10 +11036,6 @@ packages: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} - teeny-request@9.0.0: - resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} - engines: {node: '>=14'} - terser@5.36.0: resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==} engines: {node: '>=10'} @@ -12312,55 +12047,7 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-ec2@3.843.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.840.0 - '@aws-sdk/credential-provider-node': 3.840.0 - '@aws-sdk/middleware-host-header': 3.840.0 - '@aws-sdk/middleware-logger': 3.840.0 - '@aws-sdk/middleware-recursion-detection': 3.840.0 - '@aws-sdk/middleware-sdk-ec2': 3.840.0 - '@aws-sdk/middleware-user-agent': 3.840.0 - '@aws-sdk/region-config-resolver': 3.840.0 - '@aws-sdk/types': 3.840.0 - '@aws-sdk/util-endpoints': 3.840.0 - '@aws-sdk/util-user-agent-browser': 3.840.0 - '@aws-sdk/util-user-agent-node': 3.840.0 - '@smithy/config-resolver': 4.1.4 - '@smithy/core': 3.6.0 - '@smithy/fetch-http-handler': 5.0.4 - '@smithy/hash-node': 4.0.4 - '@smithy/invalid-dependency': 4.0.4 - '@smithy/middleware-content-length': 4.0.4 - '@smithy/middleware-endpoint': 4.1.13 - '@smithy/middleware-retry': 4.1.14 - '@smithy/middleware-serde': 4.0.8 - '@smithy/middleware-stack': 4.0.4 - '@smithy/node-config-provider': 4.1.3 - '@smithy/node-http-handler': 4.0.6 - '@smithy/protocol-http': 5.1.2 - '@smithy/smithy-client': 4.4.5 - '@smithy/types': 4.3.1 - '@smithy/url-parser': 4.0.4 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.21 - '@smithy/util-defaults-mode-node': 4.0.21 - '@smithy/util-endpoints': 3.0.6 - '@smithy/util-middleware': 4.0.4 - '@smithy/util-retry': 4.0.6 - '@smithy/util-utf8': 4.0.0 - '@smithy/util-waiter': 4.0.6 - '@types/uuid': 9.0.8 - tslib: 2.8.0 - uuid: 9.0.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-eks@3.840.0': + '@aws-sdk/client-iam@3.840.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -12401,18 +12088,15 @@ snapshots: '@smithy/util-retry': 4.0.6 '@smithy/util-utf8': 4.0.0 '@smithy/util-waiter': 4.0.6 - '@types/uuid': 9.0.8 tslib: 2.8.0 - uuid: 9.0.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-iam@3.840.0': + '@aws-sdk/client-sso@3.840.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/core': 3.840.0 - '@aws-sdk/credential-provider-node': 3.840.0 '@aws-sdk/middleware-host-header': 3.840.0 '@aws-sdk/middleware-logger': 3.840.0 '@aws-sdk/middleware-recursion-detection': 3.840.0 @@ -12447,95 +12131,94 @@ snapshots: '@smithy/util-middleware': 4.0.4 '@smithy/util-retry': 4.0.6 '@smithy/util-utf8': 4.0.0 - '@smithy/util-waiter': 4.0.6 - tslib: 2.8.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.840.0': + '@aws-sdk/client-sso@3.911.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.840.0 - '@aws-sdk/middleware-host-header': 3.840.0 - '@aws-sdk/middleware-logger': 3.840.0 - '@aws-sdk/middleware-recursion-detection': 3.840.0 - '@aws-sdk/middleware-user-agent': 3.840.0 - '@aws-sdk/region-config-resolver': 3.840.0 - '@aws-sdk/types': 3.840.0 - '@aws-sdk/util-endpoints': 3.840.0 - '@aws-sdk/util-user-agent-browser': 3.840.0 - '@aws-sdk/util-user-agent-node': 3.840.0 - '@smithy/config-resolver': 4.1.4 - '@smithy/core': 3.6.0 - '@smithy/fetch-http-handler': 5.0.4 - '@smithy/hash-node': 4.0.4 - '@smithy/invalid-dependency': 4.0.4 - '@smithy/middleware-content-length': 4.0.4 - '@smithy/middleware-endpoint': 4.1.13 - '@smithy/middleware-retry': 4.1.14 - '@smithy/middleware-serde': 4.0.8 - '@smithy/middleware-stack': 4.0.4 - '@smithy/node-config-provider': 4.1.3 - '@smithy/node-http-handler': 4.0.6 - '@smithy/protocol-http': 5.1.2 - '@smithy/smithy-client': 4.4.5 - '@smithy/types': 4.3.1 - '@smithy/url-parser': 4.0.4 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.21 - '@smithy/util-defaults-mode-node': 4.0.21 - '@smithy/util-endpoints': 3.0.6 - '@smithy/util-middleware': 4.0.4 - '@smithy/util-retry': 4.0.6 - '@smithy/util-utf8': 4.0.0 + '@aws-sdk/core': 3.911.0 + '@aws-sdk/middleware-host-header': 3.910.0 + '@aws-sdk/middleware-logger': 3.910.0 + '@aws-sdk/middleware-recursion-detection': 3.910.0 + '@aws-sdk/middleware-user-agent': 3.911.0 + '@aws-sdk/region-config-resolver': 3.910.0 + '@aws-sdk/types': 3.910.0 + '@aws-sdk/util-endpoints': 3.910.0 + '@aws-sdk/util-user-agent-browser': 3.910.0 + '@aws-sdk/util-user-agent-node': 3.911.0 + '@smithy/config-resolver': 4.3.3 + '@smithy/core': 3.17.0 + '@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.4 + '@smithy/middleware-retry': 4.4.4 + '@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.2 + '@smithy/protocol-http': 5.3.3 + '@smithy/smithy-client': 4.9.0 + '@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.3 + '@smithy/util-defaults-mode-node': 4.2.4 + '@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.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.840.0': + '@aws-sdk/client-sts@3.911.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.840.0 - '@aws-sdk/credential-provider-node': 3.840.0 - '@aws-sdk/middleware-host-header': 3.840.0 - '@aws-sdk/middleware-logger': 3.840.0 - '@aws-sdk/middleware-recursion-detection': 3.840.0 - '@aws-sdk/middleware-user-agent': 3.840.0 - '@aws-sdk/region-config-resolver': 3.840.0 - '@aws-sdk/types': 3.840.0 - '@aws-sdk/util-endpoints': 3.840.0 - '@aws-sdk/util-user-agent-browser': 3.840.0 - '@aws-sdk/util-user-agent-node': 3.840.0 - '@smithy/config-resolver': 4.1.4 - '@smithy/core': 3.6.0 - '@smithy/fetch-http-handler': 5.0.4 - '@smithy/hash-node': 4.0.4 - '@smithy/invalid-dependency': 4.0.4 - '@smithy/middleware-content-length': 4.0.4 - '@smithy/middleware-endpoint': 4.1.13 - '@smithy/middleware-retry': 4.1.14 - '@smithy/middleware-serde': 4.0.8 - '@smithy/middleware-stack': 4.0.4 - '@smithy/node-config-provider': 4.1.3 - '@smithy/node-http-handler': 4.0.6 - '@smithy/protocol-http': 5.1.2 - '@smithy/smithy-client': 4.4.5 - '@smithy/types': 4.3.1 - '@smithy/url-parser': 4.0.4 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.21 - '@smithy/util-defaults-mode-node': 4.0.21 - '@smithy/util-endpoints': 3.0.6 - '@smithy/util-middleware': 4.0.4 - '@smithy/util-retry': 4.0.6 - '@smithy/util-utf8': 4.0.0 - tslib: 2.8.0 + '@aws-sdk/core': 3.911.0 + '@aws-sdk/credential-provider-node': 3.911.0 + '@aws-sdk/middleware-host-header': 3.910.0 + '@aws-sdk/middleware-logger': 3.910.0 + '@aws-sdk/middleware-recursion-detection': 3.910.0 + '@aws-sdk/middleware-user-agent': 3.911.0 + '@aws-sdk/region-config-resolver': 3.910.0 + '@aws-sdk/types': 3.910.0 + '@aws-sdk/util-endpoints': 3.910.0 + '@aws-sdk/util-user-agent-browser': 3.910.0 + '@aws-sdk/util-user-agent-node': 3.911.0 + '@smithy/config-resolver': 4.3.3 + '@smithy/core': 3.17.0 + '@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.4 + '@smithy/middleware-retry': 4.4.4 + '@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.2 + '@smithy/protocol-http': 5.3.3 + '@smithy/smithy-client': 4.9.0 + '@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.3 + '@smithy/util-defaults-mode-node': 4.2.4 + '@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.8.1 transitivePeerDependencies: - aws-crt @@ -12557,6 +12240,22 @@ snapshots: fast-xml-parser: 4.4.1 tslib: 2.8.0 + '@aws-sdk/core@3.911.0': + dependencies: + '@aws-sdk/types': 3.910.0 + '@aws-sdk/xml-builder': 3.911.0 + '@smithy/core': 3.17.0 + '@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.0 + '@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.8.1 + '@aws-sdk/credential-provider-env@3.840.0': dependencies: '@aws-sdk/core': 3.840.0 @@ -12565,6 +12264,14 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.911.0': + dependencies: + '@aws-sdk/core': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@smithy/property-provider': 4.2.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.840.0': dependencies: '@aws-sdk/core': 3.840.0 @@ -12578,6 +12285,19 @@ snapshots: '@smithy/util-stream': 4.2.2 tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.911.0': + dependencies: + '@aws-sdk/core': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@smithy/fetch-http-handler': 5.3.4 + '@smithy/node-http-handler': 4.4.2 + '@smithy/property-provider': 4.2.3 + '@smithy/protocol-http': 5.3.3 + '@smithy/smithy-client': 4.9.0 + '@smithy/types': 4.8.0 + '@smithy/util-stream': 4.5.3 + tslib: 2.8.1 + '@aws-sdk/credential-provider-ini@3.840.0': dependencies: '@aws-sdk/core': 3.840.0 @@ -12596,6 +12316,24 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-ini@3.911.0': + dependencies: + '@aws-sdk/core': 3.911.0 + '@aws-sdk/credential-provider-env': 3.911.0 + '@aws-sdk/credential-provider-http': 3.911.0 + '@aws-sdk/credential-provider-process': 3.911.0 + '@aws-sdk/credential-provider-sso': 3.911.0 + '@aws-sdk/credential-provider-web-identity': 3.911.0 + '@aws-sdk/nested-clients': 3.911.0 + '@aws-sdk/types': 3.910.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.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-node@3.840.0': dependencies: '@aws-sdk/credential-provider-env': 3.840.0 @@ -12613,6 +12351,23 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-node@3.911.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.911.0 + '@aws-sdk/credential-provider-http': 3.911.0 + '@aws-sdk/credential-provider-ini': 3.911.0 + '@aws-sdk/credential-provider-process': 3.911.0 + '@aws-sdk/credential-provider-sso': 3.911.0 + '@aws-sdk/credential-provider-web-identity': 3.911.0 + '@aws-sdk/types': 3.910.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.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-process@3.840.0': dependencies: '@aws-sdk/core': 3.840.0 @@ -12622,6 +12377,15 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-process@3.911.0': + dependencies: + '@aws-sdk/core': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@smithy/property-provider': 4.2.3 + '@smithy/shared-ini-file-loader': 4.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-sso@3.840.0': dependencies: '@aws-sdk/client-sso': 3.840.0 @@ -12635,6 +12399,19 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-sso@3.911.0': + dependencies: + '@aws-sdk/client-sso': 3.911.0 + '@aws-sdk/core': 3.911.0 + '@aws-sdk/token-providers': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@smithy/property-provider': 4.2.3 + '@smithy/shared-ini-file-loader': 4.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-web-identity@3.840.0': dependencies: '@aws-sdk/core': 3.840.0 @@ -12646,6 +12423,18 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-web-identity@3.911.0': + dependencies: + '@aws-sdk/core': 3.911.0 + '@aws-sdk/nested-clients': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@smithy/property-provider': 4.2.3 + '@smithy/shared-ini-file-loader': 4.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/middleware-host-header@3.840.0': dependencies: '@aws-sdk/types': 3.840.0 @@ -12653,12 +12442,25 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@aws-sdk/middleware-host-header@3.910.0': + dependencies: + '@aws-sdk/types': 3.910.0 + '@smithy/protocol-http': 5.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/middleware-logger@3.840.0': dependencies: '@aws-sdk/types': 3.840.0 '@smithy/types': 4.3.1 tslib: 2.8.0 + '@aws-sdk/middleware-logger@3.910.0': + dependencies: + '@aws-sdk/types': 3.910.0 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/middleware-recursion-detection@3.840.0': dependencies: '@aws-sdk/types': 3.840.0 @@ -12666,16 +12468,13 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.0 - '@aws-sdk/middleware-sdk-ec2@3.840.0': + '@aws-sdk/middleware-recursion-detection@3.910.0': dependencies: - '@aws-sdk/types': 3.840.0 - '@aws-sdk/util-format-url': 3.840.0 - '@smithy/middleware-endpoint': 4.1.13 - '@smithy/protocol-http': 5.1.2 - '@smithy/signature-v4': 5.1.2 - '@smithy/smithy-client': 4.4.5 - '@smithy/types': 4.3.1 - tslib: 2.8.0 + '@aws-sdk/types': 3.910.0 + '@aws/lambda-invoke-store': 0.0.1 + '@smithy/protocol-http': 5.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 '@aws-sdk/middleware-user-agent@3.840.0': dependencies: @@ -12687,6 +12486,16 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@aws-sdk/middleware-user-agent@3.911.0': + dependencies: + '@aws-sdk/core': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@aws-sdk/util-endpoints': 3.910.0 + '@smithy/core': 3.17.0 + '@smithy/protocol-http': 5.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/nested-clients@3.840.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -12730,6 +12539,49 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/nested-clients@3.911.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.911.0 + '@aws-sdk/middleware-host-header': 3.910.0 + '@aws-sdk/middleware-logger': 3.910.0 + '@aws-sdk/middleware-recursion-detection': 3.910.0 + '@aws-sdk/middleware-user-agent': 3.911.0 + '@aws-sdk/region-config-resolver': 3.910.0 + '@aws-sdk/types': 3.910.0 + '@aws-sdk/util-endpoints': 3.910.0 + '@aws-sdk/util-user-agent-browser': 3.910.0 + '@aws-sdk/util-user-agent-node': 3.911.0 + '@smithy/config-resolver': 4.3.3 + '@smithy/core': 3.17.0 + '@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.4 + '@smithy/middleware-retry': 4.4.4 + '@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.2 + '@smithy/protocol-http': 5.3.3 + '@smithy/smithy-client': 4.9.0 + '@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.3 + '@smithy/util-defaults-mode-node': 4.2.4 + '@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.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/region-config-resolver@3.840.0': dependencies: '@aws-sdk/types': 3.840.0 @@ -12739,6 +12591,15 @@ snapshots: '@smithy/util-middleware': 4.0.4 tslib: 2.8.0 + '@aws-sdk/region-config-resolver@3.910.0': + dependencies: + '@aws-sdk/types': 3.910.0 + '@smithy/node-config-provider': 4.3.3 + '@smithy/types': 4.8.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.3 + tslib: 2.8.1 + '@aws-sdk/token-providers@3.840.0': dependencies: '@aws-sdk/core': 3.840.0 @@ -12751,11 +12612,28 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/token-providers@3.911.0': + dependencies: + '@aws-sdk/core': 3.911.0 + '@aws-sdk/nested-clients': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@smithy/property-provider': 4.2.3 + '@smithy/shared-ini-file-loader': 4.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/types@3.840.0': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@aws-sdk/types@3.910.0': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/util-endpoints@3.840.0': dependencies: '@aws-sdk/types': 3.840.0 @@ -12763,11 +12641,12 @@ snapshots: '@smithy/util-endpoints': 3.0.6 tslib: 2.8.0 - '@aws-sdk/util-format-url@3.840.0': + '@aws-sdk/util-endpoints@3.910.0': dependencies: - '@aws-sdk/types': 3.840.0 - '@smithy/querystring-builder': 4.0.4 - '@smithy/types': 4.3.1 + '@aws-sdk/types': 3.910.0 + '@smithy/types': 4.8.0 + '@smithy/url-parser': 4.2.3 + '@smithy/util-endpoints': 3.2.3 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.693.0': @@ -12781,6 +12660,13 @@ snapshots: bowser: 2.11.0 tslib: 2.8.0 + '@aws-sdk/util-user-agent-browser@3.910.0': + dependencies: + '@aws-sdk/types': 3.910.0 + '@smithy/types': 4.8.0 + bowser: 2.11.0 + tslib: 2.8.1 + '@aws-sdk/util-user-agent-node@3.840.0': dependencies: '@aws-sdk/middleware-user-agent': 3.840.0 @@ -12789,148 +12675,26 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.0 - '@aws-sdk/xml-builder@3.821.0': - dependencies: - '@smithy/types': 4.3.1 - tslib: 2.8.1 - - '@azure/abort-controller@2.1.2': - dependencies: - tslib: 2.8.1 - - '@azure/arm-containerservice@21.6.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.9.0 - '@azure/core-client': 1.9.4 - '@azure/core-lro': 2.7.2 - '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.21.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-auth@1.9.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.11.0 - tslib: 2.8.1 - - '@azure/core-client@1.9.2': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.9.0 - '@azure/core-rest-pipeline': 1.18.1 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.1.4 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-client@1.9.4': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.9.0 - '@azure/core-rest-pipeline': 1.21.0 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.12.0 - '@azure/logger': 1.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-lro@2.7.2': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.1.4 - tslib: 2.8.1 - - '@azure/core-paging@1.6.2': + '@aws-sdk/util-user-agent-node@3.911.0': dependencies: + '@aws-sdk/middleware-user-agent': 3.911.0 + '@aws-sdk/types': 3.910.0 + '@smithy/node-config-provider': 4.3.3 + '@smithy/types': 4.8.0 tslib: 2.8.1 - '@azure/core-rest-pipeline@1.18.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.9.0 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.1.4 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-rest-pipeline@1.21.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.9.0 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.12.0 - '@azure/logger': 1.2.0 - '@typespec/ts-http-runtime': 0.2.3 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-tracing@1.2.0': - dependencies: - tslib: 2.8.1 - - '@azure/core-util@1.11.0': - dependencies: - '@azure/abort-controller': 2.1.2 - tslib: 2.8.1 - - '@azure/core-util@1.12.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.2.3 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/identity@4.10.2': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.9.0 - '@azure/core-client': 1.9.2 - '@azure/core-rest-pipeline': 1.18.1 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.1.4 - '@azure/msal-browser': 4.15.0 - '@azure/msal-node': 3.6.3 - open: 10.1.2 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/logger@1.1.4': + '@aws-sdk/xml-builder@3.821.0': dependencies: + '@smithy/types': 4.3.1 tslib: 2.8.1 - '@azure/logger@1.2.0': + '@aws-sdk/xml-builder@3.911.0': dependencies: - '@typespec/ts-http-runtime': 0.2.3 + '@smithy/types': 4.8.0 + fast-xml-parser: 5.2.5 tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/msal-browser@4.15.0': - dependencies: - '@azure/msal-common': 15.8.1 - '@azure/msal-common@15.8.1': {} - - '@azure/msal-node@3.6.3': - dependencies: - '@azure/msal-common': 15.8.1 - jsonwebtoken: 9.0.2 - uuid: 8.3.2 + '@aws/lambda-invoke-store@0.0.1': {} '@babel/code-frame@7.24.7': dependencies: @@ -13705,37 +13469,11 @@ snapshots: dependencies: tslib: 2.8.1 - '@google-cloud/compute@4.12.0': - dependencies: - google-gax: 4.4.1 - transitivePeerDependencies: - - encoding - - supports-color - - '@google-cloud/container@5.19.0': - dependencies: - google-gax: 4.4.1 - transitivePeerDependencies: - - encoding - - supports-color - - '@grpc/grpc-js@1.11.3': - dependencies: - '@grpc/proto-loader': 0.7.13 - '@js-sdsl/ordered-map': 4.4.2 - '@grpc/grpc-js@1.14.0': dependencies: '@grpc/proto-loader': 0.8.0 '@js-sdsl/ordered-map': 4.4.2 - '@grpc/proto-loader@0.7.13': - dependencies: - lodash.camelcase: 4.3.0 - long: 5.2.3 - protobufjs: 7.4.0 - yargs: 17.7.2 - '@grpc/proto-loader@0.8.0': dependencies: lodash.camelcase: 4.3.0 @@ -13860,8 +13598,6 @@ snapshots: dependencies: '@swc/helpers': 0.5.17 - '@ioredis/commands@1.2.0': {} - '@isaacs/balanced-match@4.0.1': {} '@isaacs/brace-expansion@5.0.0': @@ -13989,24 +13725,6 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - optional: true - '@nestjs/axios@4.0.0(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.10.0)(rxjs@7.8.2)': dependencies: '@nestjs/common': 11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -16883,6 +16601,11 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.1 + '@smithy/abort-controller@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/config-resolver@4.1.4': dependencies: '@smithy/node-config-provider': 4.1.3 @@ -16891,6 +16614,27 @@ snapshots: '@smithy/util-middleware': 4.0.4 tslib: 2.8.0 + '@smithy/config-resolver@4.3.3': + dependencies: + '@smithy/node-config-provider': 4.3.3 + '@smithy/types': 4.8.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.3 + tslib: 2.8.1 + + '@smithy/core@3.17.0': + 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.3 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + '@smithy/core@3.6.0': dependencies: '@smithy/middleware-serde': 4.0.8 @@ -16911,6 +16655,14 @@ snapshots: '@smithy/url-parser': 4.0.4 tslib: 2.8.1 + '@smithy/credential-provider-imds@4.2.3': + 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.8.1 + '@smithy/fetch-http-handler@5.0.4': dependencies: '@smithy/protocol-http': 5.1.2 @@ -16919,6 +16671,14 @@ snapshots: '@smithy/util-base64': 4.0.0 tslib: 2.8.0 + '@smithy/fetch-http-handler@5.3.4': + 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.8.1 + '@smithy/hash-node@4.0.4': dependencies: '@smithy/types': 4.3.1 @@ -16926,11 +16686,23 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.0 + '@smithy/hash-node@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + '@smithy/invalid-dependency@4.0.4': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/invalid-dependency@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 @@ -16939,12 +16711,22 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/is-array-buffer@4.2.0': + dependencies: + tslib: 2.8.1 + '@smithy/middleware-content-length@4.0.4': dependencies: '@smithy/protocol-http': 5.1.2 '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/middleware-content-length@4.2.3': + dependencies: + '@smithy/protocol-http': 5.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/middleware-endpoint@4.1.13': dependencies: '@smithy/core': 3.6.0 @@ -16956,6 +16738,17 @@ snapshots: '@smithy/util-middleware': 4.0.4 tslib: 2.8.0 + '@smithy/middleware-endpoint@4.3.4': + dependencies: + '@smithy/core': 3.17.0 + '@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.8.1 + '@smithy/middleware-retry@4.1.14': dependencies: '@smithy/node-config-provider': 4.1.3 @@ -16968,17 +16761,40 @@ snapshots: tslib: 2.8.0 uuid: 9.0.1 + '@smithy/middleware-retry@4.4.4': + 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.0 + '@smithy/types': 4.8.0 + '@smithy/util-middleware': 4.2.3 + '@smithy/util-retry': 4.2.3 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + '@smithy/middleware-serde@4.0.8': dependencies: '@smithy/protocol-http': 5.1.2 '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/middleware-serde@4.2.3': + dependencies: + '@smithy/protocol-http': 5.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/middleware-stack@4.0.4': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/middleware-stack@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/node-config-provider@4.1.3': dependencies: '@smithy/property-provider': 4.0.4 @@ -16986,6 +16802,13 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/node-config-provider@4.3.3': + dependencies: + '@smithy/property-provider': 4.2.3 + '@smithy/shared-ini-file-loader': 4.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/node-http-handler@4.0.6': dependencies: '@smithy/abort-controller': 4.0.4 @@ -16994,36 +16817,74 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/node-http-handler@4.4.2': + 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.8.1 + '@smithy/property-provider@4.0.4': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.1 + '@smithy/property-provider@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/protocol-http@5.1.2': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/protocol-http@5.3.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/querystring-builder@4.0.4': dependencies: '@smithy/types': 4.3.1 '@smithy/util-uri-escape': 4.0.0 tslib: 2.8.1 + '@smithy/querystring-builder@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + '@smithy/util-uri-escape': 4.2.0 + tslib: 2.8.1 + '@smithy/querystring-parser@4.0.4': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.1 + '@smithy/querystring-parser@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/service-error-classification@4.0.6': dependencies: '@smithy/types': 4.3.1 + '@smithy/service-error-classification@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + '@smithy/shared-ini-file-loader@4.0.4': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.1 + '@smithy/shared-ini-file-loader@4.3.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/signature-v4@5.1.2': dependencies: '@smithy/is-array-buffer': 4.0.0 @@ -17035,6 +16896,17 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 + '@smithy/signature-v4@5.3.3': + 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.8.1 + '@smithy/smithy-client@4.4.5': dependencies: '@smithy/core': 3.6.0 @@ -17045,34 +16917,64 @@ snapshots: '@smithy/util-stream': 4.2.2 tslib: 2.8.0 - '@smithy/types@3.7.2': + '@smithy/smithy-client@4.9.0': dependencies: + '@smithy/core': 3.17.0 + '@smithy/middleware-endpoint': 4.3.4 + '@smithy/middleware-stack': 4.2.3 + '@smithy/protocol-http': 5.3.3 + '@smithy/types': 4.8.0 + '@smithy/util-stream': 4.5.3 tslib: 2.8.1 '@smithy/types@4.3.1': dependencies: tslib: 2.8.0 + '@smithy/types@4.8.0': + dependencies: + tslib: 2.8.1 + '@smithy/url-parser@4.0.4': dependencies: '@smithy/querystring-parser': 4.0.4 '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/url-parser@4.2.3': + dependencies: + '@smithy/querystring-parser': 4.2.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/util-base64@4.0.0': dependencies: '@smithy/util-buffer-from': 4.0.0 '@smithy/util-utf8': 4.0.0 tslib: 2.8.0 + '@smithy/util-base64@4.3.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + '@smithy/util-body-length-browser@4.0.0': dependencies: tslib: 2.8.0 + '@smithy/util-body-length-browser@4.2.0': + dependencies: + tslib: 2.8.1 + '@smithy/util-body-length-node@4.0.0': dependencies: tslib: 2.8.0 + '@smithy/util-body-length-node@4.2.1': + dependencies: + tslib: 2.8.1 + '@smithy/util-buffer-from@2.2.0': dependencies: '@smithy/is-array-buffer': 2.2.0 @@ -17083,10 +16985,19 @@ snapshots: '@smithy/is-array-buffer': 4.0.0 tslib: 2.8.1 + '@smithy/util-buffer-from@4.2.0': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + tslib: 2.8.1 + '@smithy/util-config-provider@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-config-provider@4.2.0': + dependencies: + tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.0.21': dependencies: '@smithy/property-provider': 4.0.4 @@ -17095,6 +17006,13 @@ snapshots: bowser: 2.11.0 tslib: 2.8.0 + '@smithy/util-defaults-mode-browser@4.3.3': + dependencies: + '@smithy/property-provider': 4.2.3 + '@smithy/smithy-client': 4.9.0 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/util-defaults-mode-node@4.0.21': dependencies: '@smithy/config-resolver': 4.1.4 @@ -17105,27 +17023,58 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/util-defaults-mode-node@4.2.4': + dependencies: + '@smithy/config-resolver': 4.3.3 + '@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.0 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/util-endpoints@3.0.6': dependencies: '@smithy/node-config-provider': 4.1.3 '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/util-endpoints@3.2.3': + dependencies: + '@smithy/node-config-provider': 4.3.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/util-hex-encoding@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-hex-encoding@4.2.0': + dependencies: + tslib: 2.8.1 + '@smithy/util-middleware@4.0.4': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/util-middleware@4.2.3': + dependencies: + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/util-retry@4.0.6': dependencies: '@smithy/service-error-classification': 4.0.6 '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/util-retry@4.2.3': + dependencies: + '@smithy/service-error-classification': 4.2.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@smithy/util-stream@4.2.2': dependencies: '@smithy/fetch-http-handler': 5.0.4 @@ -17137,10 +17086,25 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 + '@smithy/util-stream@4.5.3': + dependencies: + '@smithy/fetch-http-handler': 5.3.4 + '@smithy/node-http-handler': 4.4.2 + '@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.8.1 + '@smithy/util-uri-escape@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-uri-escape@4.2.0': + dependencies: + tslib: 2.8.1 + '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 @@ -17151,12 +17115,21 @@ snapshots: '@smithy/util-buffer-from': 4.0.0 tslib: 2.8.0 + '@smithy/util-utf8@4.2.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + tslib: 2.8.1 + '@smithy/util-waiter@4.0.6': dependencies: '@smithy/abort-controller': 4.0.4 '@smithy/types': 4.3.1 tslib: 2.8.0 + '@smithy/uuid@1.1.0': + dependencies: + tslib: 2.8.1 + '@socket.io/component-emitter@3.1.2': {} '@swagger-api/apidom-ast@1.0.0-beta.44': @@ -17581,8 +17554,6 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@tootallnate/once@2.0.0': {} - '@tootallnate/quickjs-emscripten@0.23.0': {} '@tree-sitter-grammars/tree-sitter-yaml@0.7.1(tree-sitter@0.22.4)': @@ -17662,8 +17633,6 @@ snapshots: dependencies: '@types/node': 22.16.2 - '@types/caseless@0.12.5': {} - '@types/connect@3.4.36': dependencies: '@types/node': 22.16.2 @@ -17847,8 +17816,6 @@ snapshots: '@types/lodash@4.17.12': {} - '@types/long@4.0.2': {} - '@types/luxon@3.4.2': {} '@types/memcached@2.2.10': @@ -17923,13 +17890,6 @@ snapshots: dependencies: csstype: 3.1.3 - '@types/request@2.48.12': - dependencies: - '@types/caseless': 0.12.5 - '@types/node': 22.16.2 - '@types/tough-cookie': 4.0.5 - form-data: 2.5.3 - '@types/retry@0.12.2': {} '@types/semver@7.7.0': {} @@ -17961,8 +17921,6 @@ snapshots: '@types/tinycolor2@1.4.6': {} - '@types/tough-cookie@4.0.5': {} - '@types/triple-beam@1.3.5': {} '@types/trusted-types@2.0.7': @@ -17974,8 +17932,6 @@ snapshots: '@types/uuid@10.0.0': {} - '@types/uuid@9.0.8': {} - '@types/ws@8.18.1': dependencies: '@types/node': 22.16.2 @@ -18099,14 +18055,6 @@ snapshots: '@typescript-eslint/types': 8.13.0 eslint-visitor-keys: 3.4.3 - '@typespec/ts-http-runtime@0.2.3': - dependencies: - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - '@vercel/ncc@0.38.3': {} '@vitest/expect@2.1.9': @@ -18273,12 +18221,6 @@ snapshots: add@2.0.6: {} - agent-base@6.0.2: - dependencies: - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - agent-base@7.1.1: dependencies: debug: 4.4.1 @@ -18622,29 +18564,6 @@ snapshots: hexer: 1.5.0 xtend: 4.0.2 - bullmq-otel@1.0.1: - dependencies: - '@opentelemetry/api': 1.9.0 - bullmq: 5.48.1 - transitivePeerDependencies: - - supports-color - - bullmq@5.48.1: - dependencies: - cron-parser: 4.9.0 - ioredis: 5.4.1 - msgpackr: 1.11.2 - node-abort-controller: 3.1.1 - semver: 7.7.1 - tslib: 2.8.1 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - - bundle-name@4.1.0: - dependencies: - run-applescript: 7.0.0 - bundle-require@5.1.0(esbuild@0.25.6): dependencies: esbuild: 0.25.6 @@ -18815,8 +18734,6 @@ snapshots: clsx@2.1.1: {} - cluster-key-slot@1.1.2: {} - cmdk@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.8)(react@19.0.0) @@ -18950,10 +18867,6 @@ snapshots: create-require@1.1.1: {} - cron-parser@4.9.0: - dependencies: - luxon: 3.6.1 - cron@3.5.0: dependencies: '@types/luxon': 3.4.2 @@ -19151,13 +19064,6 @@ snapshots: deepmerge@4.3.1: {} - default-browser-id@5.0.0: {} - - default-browser@5.2.1: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.0 - defaults@1.0.4: dependencies: clone: 1.0.4 @@ -19168,8 +19074,6 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - define-lazy-prop@3.0.0: {} - define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -19195,8 +19099,6 @@ snapshots: delayed-stream@1.0.0: {} - denque@2.1.0: {} - depd@2.0.0: {} deprecation@2.3.1: {} @@ -19301,13 +19203,6 @@ snapshots: duplexer@0.1.2: {} - duplexify@4.1.3: - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-shift: 1.0.3 - eastasianwidth@0.2.0: {} easy-table@1.1.0: @@ -19337,10 +19232,6 @@ snapshots: encodeurl@2.0.0: {} - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - engine.io-parser@5.2.3: {} engine.io@6.6.4: @@ -20087,6 +19978,10 @@ snapshots: dependencies: strnum: 1.1.2 + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.1.1 + fastest-stable-stringify@2.0.2: {} fastq@1.19.1: @@ -20181,14 +20076,6 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 - form-data@2.5.3: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - mime-types: 2.1.35 - safe-buffer: 5.2.1 - form-data@4.0.3: dependencies: asynckit: 0.4.0 @@ -20440,24 +20327,6 @@ snapshots: - encoding - supports-color - google-gax@4.4.1: - dependencies: - '@grpc/grpc-js': 1.11.3 - '@grpc/proto-loader': 0.7.13 - '@types/long': 4.0.2 - abort-controller: 3.0.0 - duplexify: 4.1.3 - google-auth-library: 9.15.1 - node-fetch: 2.7.0 - object-hash: 3.0.0 - proto3-json-serializer: 2.0.2 - protobufjs: 7.4.0 - retry-request: 7.0.2 - uuid: 9.0.1 - transitivePeerDependencies: - - encoding - - supports-color - googleapis-common@7.2.0: dependencies: extend: 3.0.2 @@ -20613,14 +20482,6 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 - http-proxy-agent@5.0.0: - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 @@ -20636,13 +20497,6 @@ snapshots: http-status@1.8.1: {} - https-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 @@ -20766,20 +20620,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - ioredis@5.4.1: - dependencies: - '@ioredis/commands': 1.2.0 - cluster-key-slot: 1.1.2 - debug: 4.4.1 - 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 - ip-address@9.0.5: dependencies: jsbn: 1.1.0 @@ -20864,8 +20704,6 @@ snapshots: is-decimal@1.0.4: {} - is-docker@3.0.0: {} - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -20887,10 +20725,6 @@ snapshots: is-hexadecimal@1.0.4: {} - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - is-interactive@1.0.0: {} is-lower-case@1.1.3: @@ -21003,10 +20837,6 @@ snapshots: is-what@4.1.16: {} - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 - isarray@2.0.5: {} isbinaryfile@4.0.10: {} @@ -21160,19 +20990,6 @@ snapshots: jsonpointer@5.0.1: {} - jsonwebtoken@9.0.2: - 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.1.1 - ms: 2.1.3 - semver: 7.7.1 - jsprim@1.4.2: dependencies: assert-plus: 1.0.0 @@ -21187,23 +21004,12 @@ snapshots: object.assign: 4.1.5 object.values: 1.2.1 - jwa@1.4.2: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - jwa@2.0.0: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - jws@3.2.2: - dependencies: - jwa: 1.4.2 - safe-buffer: 5.2.1 - jws@4.0.0: dependencies: jwa: 2.0.0 @@ -21250,28 +21056,12 @@ snapshots: lodash.debounce@4.0.8: {} - lodash.defaults@4.2.0: {} - lodash.get@4.4.2: {} - lodash.includes@4.3.0: {} - - lodash.isarguments@3.1.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.merge@4.6.2: {} - lodash.once@4.1.1: {} - lodash.sortby@4.7.0: {} lodash@4.17.21: {} @@ -21340,8 +21130,6 @@ snapshots: luxon@3.5.0: {} - luxon@3.6.1: {} - lz-string@1.5.0: {} magic-string@0.30.17: @@ -21447,22 +21235,6 @@ snapshots: ms@2.1.3: {} - msgpackr-extract@3.0.3: - dependencies: - node-gyp-build-optional-packages: 5.2.2 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 - optional: true - - msgpackr@1.11.2: - optionalDependencies: - msgpackr-extract: 3.0.3 - murmurhash-js@1.0.0: {} murmurhash@2.0.1: {} @@ -21626,11 +21398,6 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-gyp-build-optional-packages@5.2.2: - dependencies: - detect-libc: 2.0.3 - optional: true - node-gyp-build@4.8.4: optional: true @@ -21740,13 +21507,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - open@10.1.2: - dependencies: - default-browser: 5.2.1 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 3.1.0 - openapi-fetch@0.13.8: dependencies: openapi-typescript-helpers: 0.0.15 @@ -22117,10 +21877,6 @@ snapshots: dependencies: xtend: 4.0.2 - proto3-json-serializer@2.0.2: - dependencies: - protobufjs: 7.4.0 - protobufjs@7.4.0: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -22557,20 +22313,6 @@ snapshots: tiny-invariant: 1.3.3 victory-vendor: 36.9.2 - redis-errors@1.2.0: {} - - redis-parser@3.0.0: - dependencies: - redis-errors: 1.2.0 - - redis-semaphore@5.6.2(ioredis@5.4.1): - dependencies: - debug: 4.4.1 - optionalDependencies: - ioredis: 5.4.1 - transitivePeerDependencies: - - supports-color - redux-immutable@4.0.0(immutable@3.8.2): dependencies: immutable: 3.8.2 @@ -22703,15 +22445,6 @@ snapshots: ret@0.2.2: {} - retry-request@7.0.2: - dependencies: - '@types/request': 2.48.12 - extend: 3.0.2 - teeny-request: 9.0.0 - transitivePeerDependencies: - - encoding - - supports-color - retry@0.13.1: {} reusify@1.1.0: {} @@ -22766,8 +22499,6 @@ snapshots: dependencies: '@babel/runtime': 7.26.10 - run-applescript@7.0.0: {} - run-async@2.4.1: {} run-parallel@1.2.0: @@ -23122,8 +22853,6 @@ snapshots: stack-generator: 2.0.10 stacktrace-gps: 3.1.2 - standard-as-callback@2.1.0: {} - state-local@1.0.7: {} statuses@2.0.1: {} @@ -23137,12 +22866,6 @@ snapshots: stream-buffers@3.0.3: {} - stream-events@1.0.5: - dependencies: - stubs: 3.0.0 - - stream-shift@1.0.3: {} - streamsearch@1.1.0: {} string-template@0.2.1: {} @@ -23238,12 +22961,12 @@ snapshots: strnum@1.1.2: {} + strnum@2.1.1: {} + strtok3@10.3.1: dependencies: '@tokenizer/token': 0.3.0 - stubs@3.0.0: {} - styled-jsx@5.1.6(@babel/core@7.24.5)(react@19.0.0): dependencies: client-only: 0.0.1 @@ -23454,17 +23177,6 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 - teeny-request@9.0.0: - dependencies: - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - node-fetch: 2.7.0 - stream-events: 1.0.5 - uuid: 9.0.1 - transitivePeerDependencies: - - encoding - - supports-color - terser@5.36.0: dependencies: '@jridgewell/source-map': 0.3.11 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c8f530a0a..6daf4d857 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -23,7 +23,6 @@ catalog: "next": 15.2.1 "next-auth": "5.0.0-beta.25" "@next/eslint-plugin-next": ^15.2.2 - "bullmq": ^5.48.1 "ts-is-present": ^1.2.2 react-hook-form: ^7.54.2 semver: ^7.7.1 @@ -32,8 +31,6 @@ catalog: "@tabler/icons-react": ^3.31.0 "dotenv-cli": ^7.4.4 "@hookform/resolvers": ^3.10.0 - "redis-semaphore": ^5.6.2 - "ioredis": ^5.4.1 "ms": ^2.1.3 "@types/ms": ^0.7.34